whoopsie-0.2.24.5/0000775000000000000000000000000012321555753010477 5ustar whoopsie-0.2.24.5/lib/0000775000000000000000000000000012321555753011245 5ustar whoopsie-0.2.24.5/lib/libwhoopsie.pc0000664000000000000000000000033612061665776014127 0ustar prefix=/usr exec_prefix=${prefix} libdir=${exec_prefix}/lib bindir=${exec_prefix}/bin includedir=${prefix}/include Requires: glib-2.0 Libs: -lwhoopsie -lgcrypt Name: libwhoopsie Description: libwhoopsie. Version: 0.0.0 whoopsie-0.2.24.5/lib/bson/0000775000000000000000000000000012321555753012206 5ustar whoopsie-0.2.24.5/lib/bson/platform.h0000664000000000000000000000507012061665776014215 0ustar /** @file platform.h */ /** Copyright 2009-2011 10gen Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* all platform-specific ifdefs should go here */ #ifndef _PLATFORM_HACKS_H_ #define _PLATFORM_HACKS_H_ #ifdef __GNUC__ #define MONGO_INLINE static __inline__ #else #define MONGO_INLINE static #endif #ifdef __cplusplus #define MONGO_EXTERN_C_START extern "C" { #define MONGO_EXTERN_C_END } #else #define MONGO_EXTERN_C_START #define MONGO_EXTERN_C_END #endif #if defined(MONGO_HAVE_STDINT) || __STDC_VERSION__ >= 199901L #include #elif defined(MONGO_HAVE_UNISTD) #include #elif defined(MONGO_USE__INT64) typedef __int64 int64_t; typedef unsigned __int64 uint64_t; #elif defined(MONGO_USE_LONG_LONG_INT) typedef long long int int64_t; typedef unsigned long long int uint64_t; #else #error must have a 64bit int type #endif /* big endian is only used for OID generation. little is used everywhere else */ #ifdef MONGO_BIG_ENDIAN #define bson_little_endian64(out, in) ( bson_swap_endian64(out, in) ) #define bson_little_endian32(out, in) ( bson_swap_endian32(out, in) ) #define bson_big_endian64(out, in) ( memcpy(out, in, 8) ) #define bson_big_endian32(out, in) ( memcpy(out, in, 4) ) #else #define bson_little_endian64(out, in) ( memcpy(out, in, 8) ) #define bson_little_endian32(out, in) ( memcpy(out, in, 4) ) #define bson_big_endian64(out, in) ( bson_swap_endian64(out, in) ) #define bson_big_endian32(out, in) ( bson_swap_endian32(out, in) ) #endif MONGO_EXTERN_C_START MONGO_INLINE void bson_swap_endian64( void *outp, const void *inp ) { const char *in = ( const char * )inp; char *out = ( char * )outp; out[0] = in[7]; out[1] = in[6]; out[2] = in[5]; out[3] = in[4]; out[4] = in[3]; out[5] = in[2]; out[6] = in[1]; out[7] = in[0]; } MONGO_INLINE void bson_swap_endian32( void *outp, const void *inp ) { const char *in = ( const char * )inp; char *out = ( char * )outp; out[0] = in[3]; out[1] = in[2]; out[2] = in[1]; out[3] = in[0]; } MONGO_EXTERN_C_END #endif whoopsie-0.2.24.5/lib/bson/bson.h0000664000000000000000000006045412061665776013341 0ustar /** * @file bson.h * @brief BSON Declarations */ /* Copyright 2009-2011 10gen Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef _BSON_H_ #define _BSON_H_ #include "platform.h" #include #include #include #include #include MONGO_EXTERN_C_START #define BSON_OK 0 #define BSON_ERROR -1 enum bson_error_t { BSON_SIZE_OVERFLOW = 1 /**< Trying to create a BSON object larger than INT_MAX. */ }; enum bson_validity_t { BSON_VALID = 0, /**< BSON is valid and UTF-8 compliant. */ BSON_NOT_UTF8 = ( 1<<1 ), /**< A key or a string is not valid UTF-8. */ BSON_FIELD_HAS_DOT = ( 1<<2 ), /**< Warning: key contains '.' character. */ BSON_FIELD_INIT_DOLLAR = ( 1<<3 ), /**< Warning: key starts with '$' character. */ BSON_ALREADY_FINISHED = ( 1<<4 ) /**< Trying to modify a finished BSON object. */ }; enum bson_binary_subtype_t { BSON_BIN_BINARY = 0, BSON_BIN_FUNC = 1, BSON_BIN_BINARY_OLD = 2, BSON_BIN_UUID = 3, BSON_BIN_MD5 = 5, BSON_BIN_USER = 128 }; typedef enum { BSON_EOO = 0, BSON_DOUBLE = 1, BSON_STRING = 2, BSON_OBJECT = 3, BSON_ARRAY = 4, BSON_BINDATA = 5, BSON_UNDEFINED = 6, BSON_OID = 7, BSON_BOOL = 8, BSON_DATE = 9, BSON_NULL = 10, BSON_REGEX = 11, BSON_DBREF = 12, /**< Deprecated. */ BSON_CODE = 13, BSON_SYMBOL = 14, BSON_CODEWSCOPE = 15, BSON_INT = 16, BSON_TIMESTAMP = 17, BSON_LONG = 18 } bson_type; typedef int bson_bool_t; typedef struct { const char *cur; bson_bool_t first; } bson_iterator; typedef struct { char *data; char *cur; int dataSize; bson_bool_t finished; int stack[32]; int stackPos; int err; /**< Bitfield representing errors or warnings on this buffer */ char *errstr; /**< A string representation of the most recent error or warning. */ } bson; #pragma pack(1) typedef union { char bytes[12]; int ints[3]; } bson_oid_t; #pragma pack() typedef int64_t bson_date_t; /* milliseconds since epoch UTC */ typedef struct { int i; /* increment */ int t; /* time in seconds */ } bson_timestamp_t; /* ---------------------------- READING ------------------------------ */ /** * Size of a BSON object. * * @param b the BSON object. * * @return the size. */ int bson_size( const bson *b ); /** * Print a string representation of a BSON object. * * @param b the BSON object to print. */ void bson_print( bson *b ); /** * Return a pointer to the raw buffer stored by this bson object. * * @param b a BSON object */ const char *bson_data( bson *b ); /** * Print a string representation of a BSON object. * * @param bson the raw data to print. * @param depth the depth to recurse the object.x */ void bson_print_raw( const char *bson , int depth ); /** * Advance a bson_iterator to the named field. * * @param it the bson_iterator to use. * @param obj the BSON object to use. * @param name the name of the field to find. * * @return the type of the found object or BSON_EOO if it is not found. */ bson_type bson_find( bson_iterator *it, const bson *obj, const char *name ); /** * Initialize a bson_iterator. * * @param i the bson_iterator to initialize. * @param bson the BSON object to associate with the iterator. */ void bson_iterator_init( bson_iterator *i , const bson *b ); /** * Initialize a bson iterator from a const char* buffer. Note * that this is mostly used internally. * * @param i the bson_iterator to initialize. * @param buffer the buffer to point to. */ void bson_iterator_from_buffer( bson_iterator *i, const char *buffer ); /* more returns true for eoo. best to loop with bson_iterator_next(&it) */ /** * Check to see if the bson_iterator has more data. * * @param i the iterator. * * @return returns true if there is more data. */ bson_bool_t bson_iterator_more( const bson_iterator *i ); /** * Point the iterator at the next BSON object. * * @param i the bson_iterator. * * @return the type of the next BSON object. */ bson_type bson_iterator_next( bson_iterator *i ); /** * Get the type of the BSON object currently pointed to by the iterator. * * @param i the bson_iterator * * @return the type of the current BSON object. */ bson_type bson_iterator_type( const bson_iterator *i ); /** * Get the key of the BSON object currently pointed to by the iterator. * * @param i the bson_iterator * * @return the key of the current BSON object. */ const char *bson_iterator_key( const bson_iterator *i ); /** * Get the value of the BSON object currently pointed to by the iterator. * * @param i the bson_iterator * * @return the value of the current BSON object. */ const char *bson_iterator_value( const bson_iterator *i ); /* these convert to the right type (return 0 if non-numeric) */ /** * Get the double value of the BSON object currently pointed to by the * iterator. * * @param i the bson_iterator * * @return the value of the current BSON object. */ double bson_iterator_double( const bson_iterator *i ); /** * Get the int value of the BSON object currently pointed to by the iterator. * * @param i the bson_iterator * * @return the value of the current BSON object. */ int bson_iterator_int( const bson_iterator *i ); /** * Get the long value of the BSON object currently pointed to by the iterator. * * @param i the bson_iterator * * @return the value of the current BSON object. */ int64_t bson_iterator_long( const bson_iterator *i ); /* return the bson timestamp as a whole or in parts */ /** * Get the timestamp value of the BSON object currently pointed to by * the iterator. * * @param i the bson_iterator * * @return the value of the current BSON object. */ bson_timestamp_t bson_iterator_timestamp( const bson_iterator *i ); /** * Get the boolean value of the BSON object currently pointed to by * the iterator. * * @param i the bson_iterator * * @return the value of the current BSON object. */ /* false: boolean false, 0 in any type, or null */ /* true: anything else (even empty strings and objects) */ bson_bool_t bson_iterator_bool( const bson_iterator *i ); /** * Get the double value of the BSON object currently pointed to by the * iterator. Assumes the correct type is used. * * @param i the bson_iterator * * @return the value of the current BSON object. */ /* these assume you are using the right type */ double bson_iterator_double_raw( const bson_iterator *i ); /** * Get the int value of the BSON object currently pointed to by the * iterator. Assumes the correct type is used. * * @param i the bson_iterator * * @return the value of the current BSON object. */ int bson_iterator_int_raw( const bson_iterator *i ); /** * Get the long value of the BSON object currently pointed to by the * iterator. Assumes the correct type is used. * * @param i the bson_iterator * * @return the value of the current BSON object. */ int64_t bson_iterator_long_raw( const bson_iterator *i ); /** * Get the bson_bool_t value of the BSON object currently pointed to by the * iterator. Assumes the correct type is used. * * @param i the bson_iterator * * @return the value of the current BSON object. */ bson_bool_t bson_iterator_bool_raw( const bson_iterator *i ); /** * Get the bson_oid_t value of the BSON object currently pointed to by the * iterator. * * @param i the bson_iterator * * @return the value of the current BSON object. */ bson_oid_t *bson_iterator_oid( const bson_iterator *i ); /** * Get the string value of the BSON object currently pointed to by the * iterator. * * @param i the bson_iterator * * @return the value of the current BSON object. */ /* these can also be used with bson_code and bson_symbol*/ const char *bson_iterator_string( const bson_iterator *i ); /** * Get the string length of the BSON object currently pointed to by the * iterator. * * @param i the bson_iterator * * @return the length of the current BSON object. */ int bson_iterator_string_len( const bson_iterator *i ); /** * Get the code value of the BSON object currently pointed to by the * iterator. Works with bson_code, bson_codewscope, and BSON_STRING * returns NULL for everything else. * * @param i the bson_iterator * * @return the code value of the current BSON object. */ /* works with bson_code, bson_codewscope, and BSON_STRING */ /* returns NULL for everything else */ const char *bson_iterator_code( const bson_iterator *i ); /** * Calls bson_empty on scope if not a bson_codewscope * * @param i the bson_iterator. * @param scope the bson scope. */ /* calls bson_empty on scope if not a bson_codewscope */ void bson_iterator_code_scope( const bson_iterator *i, bson *scope ); /** * Get the date value of the BSON object currently pointed to by the * iterator. * * @param i the bson_iterator * * @return the date value of the current BSON object. */ /* both of these only work with bson_date */ bson_date_t bson_iterator_date( const bson_iterator *i ); /** * Get the time value of the BSON object currently pointed to by the * iterator. * * @param i the bson_iterator * * @return the time value of the current BSON object. */ time_t bson_iterator_time_t( const bson_iterator *i ); /** * Get the length of the BSON binary object currently pointed to by the * iterator. * * @param i the bson_iterator * * @return the length of the current BSON binary object. */ int bson_iterator_bin_len( const bson_iterator *i ); /** * Get the type of the BSON binary object currently pointed to by the * iterator. * * @param i the bson_iterator * * @return the type of the current BSON binary object. */ char bson_iterator_bin_type( const bson_iterator *i ); /** * Get the value of the BSON binary object currently pointed to by the * iterator. * * @param i the bson_iterator * * @return the value of the current BSON binary object. */ const char *bson_iterator_bin_data( const bson_iterator *i ); /** * Get the value of the BSON regex object currently pointed to by the * iterator. * * @param i the bson_iterator * * @return the value of the current BSON regex object. */ const char *bson_iterator_regex( const bson_iterator *i ); /** * Get the options of the BSON regex object currently pointed to by the * iterator. * * @param i the bson_iterator. * * @return the options of the current BSON regex object. */ const char *bson_iterator_regex_opts( const bson_iterator *i ); /* these work with BSON_OBJECT and BSON_ARRAY */ /** * Get the BSON subobject currently pointed to by the * iterator. * * @param i the bson_iterator. * @param sub the BSON subobject destination. */ void bson_iterator_subobject( const bson_iterator *i, bson *sub ); /** * Get a bson_iterator that on the BSON subobject. * * @param i the bson_iterator. * @param sub the iterator to point at the BSON subobject. */ void bson_iterator_subiterator( const bson_iterator *i, bson_iterator *sub ); /* str must be at least 24 hex chars + null byte */ /** * Create a bson_oid_t from a string. * * @param oid the bson_oid_t destination. * @param str a null terminated string comprised of at least 24 hex chars. */ void bson_oid_from_string( bson_oid_t *oid, const char *str ); /** * Create a string representation of the bson_oid_t. * * @param oid the bson_oid_t source. * @param str the string representation destination. */ void bson_oid_to_string( const bson_oid_t *oid, char *str ); /** * Create a bson_oid object. * * @param oid the destination for the newly created bson_oid_t. */ void bson_oid_gen( bson_oid_t *oid ); /** * Set a function to be used to generate the second four bytes * of an object id. * * @param func a pointer to a function that returns an int. */ void bson_set_oid_fuzz( int ( *func )( void ) ); /** * Set a function to be used to generate the incrementing part * of an object id (last four bytes). If you need thread-safety * in generating object ids, you should set this function. * * @param func a pointer to a function that returns an int. */ void bson_set_oid_inc( int ( *func )( void ) ); /** * Get the time a bson_oid_t was created. * * @param oid the bson_oid_t. */ time_t bson_oid_generated_time( bson_oid_t *oid ); /* Gives the time the OID was created */ /* ---------------------------- BUILDING ------------------------------ */ /** * Initialize a new bson object. If not created * with bson_new, you must initialize each new bson * object using this function. * * @note When finished, you must pass the bson object to * bson_destroy( ). */ void bson_init( bson *b ); /** * Initialize a BSON object, and point its data * pointer to the provided char*. * * @param b the BSON object to initialize. * @param data the raw BSON data. * * @return BSON_OK or BSON_ERROR. */ int bson_init_data( bson *b , char *data ); /** * Initialize a BSON object, and point its data * pointer to the provided char*. We assume * that the data represents a finished BSON object. * * @param b the BSON object to initialize. * @param data the raw BSON data. * * @return BSON_OK or BSON_ERROR. */ int bson_init_finished_data( bson *b, char *data ); /** * Initialize a BSON object, and set its * buffer to the given size. * * @param b the BSON object to initialize. * @param size the initial size of the buffer. * * @return BSON_OK or BSON_ERROR. */ void bson_init_size( bson *b, int size ); /** * Grow a bson object. * * @param b the bson to grow. * @param bytesNeeded the additional number of bytes needed. * * @return BSON_OK or BSON_ERROR with the bson error object set. * Exits if allocation fails. */ int bson_ensure_space( bson *b, const int bytesNeeded ); /** * Finalize a bson object. * * @param b the bson object to finalize. * * @return the standard error code. To deallocate memory, * call bson_destroy on the bson object. */ int bson_finish( bson *b ); /** * Destroy a bson object. * * @param b the bson object to destroy. * */ void bson_destroy( bson *b ); /** * Returns a pointer to a static empty BSON object. * * @param obj the BSON object to initialize. * * @return the empty initialized BSON object. */ /* returns pointer to static empty bson object */ bson *bson_empty( bson *obj ); /** * Make a complete copy of the a BSON object. * The source bson object must be in a finished * state; otherwise, the copy will fail. * * @param out the copy destination BSON object. * @param in the copy source BSON object. */ int bson_copy( bson *out, const bson *in ); /* puts data in new buffer. NOOP if out==NULL */ /** * Append a previously created bson_oid_t to a bson object. * * @param b the bson to append to. * @param name the key for the bson_oid_t. * @param oid the bson_oid_t to append. * * @return BSON_OK or BSON_ERROR. */ int bson_append_oid( bson *b, const char *name, const bson_oid_t *oid ); /** * Append a bson_oid_t to a bson. * * @param b the bson to append to. * @param name the key for the bson_oid_t. * * @return BSON_OK or BSON_ERROR. */ int bson_append_new_oid( bson *b, const char *name ); /** * Append an int to a bson. * * @param b the bson to append to. * @param name the key for the int. * @param i the int to append. * * @return BSON_OK or BSON_ERROR. */ int bson_append_int( bson *b, const char *name, const int i ); /** * Append an long to a bson. * * @param b the bson to append to. * @param name the key for the long. * @param i the long to append. * * @return BSON_OK or BSON_ERROR. */ int bson_append_long( bson *b, const char *name, const int64_t i ); /** * Append an double to a bson. * * @param b the bson to append to. * @param name the key for the double. * @param d the double to append. * * @return BSON_OK or BSON_ERROR. */ int bson_append_double( bson *b, const char *name, const double d ); /** * Append a string to a bson. * * @param b the bson to append to. * @param name the key for the string. * @param str the string to append. * * @return BSON_OK or BSON_ERROR. */ int bson_append_string( bson *b, const char *name, const char *str ); /** * Append len bytes of a string to a bson. * * @param b the bson to append to. * @param name the key for the string. * @param str the string to append. * @param len the number of bytes from str to append. * * @return BSON_OK or BSON_ERROR. */ int bson_append_string_n( bson *b, const char *name, const char *str, int len ); /** * Append a symbol to a bson. * * @param b the bson to append to. * @param name the key for the symbol. * @param str the symbol to append. * * @return BSON_OK or BSON_ERROR. */ int bson_append_symbol( bson *b, const char *name, const char *str ); /** * Append len bytes of a symbol to a bson. * * @param b the bson to append to. * @param name the key for the symbol. * @param str the symbol to append. * @param len the number of bytes from str to append. * * @return BSON_OK or BSON_ERROR. */ int bson_append_symbol_n( bson *b, const char *name, const char *str, int len ); /** * Append code to a bson. * * @param b the bson to append to. * @param name the key for the code. * @param str the code to append. * @param len the number of bytes from str to append. * * @return BSON_OK or BSON_ERROR. */ int bson_append_code( bson *b, const char *name, const char *str ); /** * Append len bytes of code to a bson. * * @param b the bson to append to. * @param name the key for the code. * @param str the code to append. * @param len the number of bytes from str to append. * * @return BSON_OK or BSON_ERROR. */ int bson_append_code_n( bson *b, const char *name, const char *str, int len ); /** * Append code to a bson with scope. * * @param b the bson to append to. * @param name the key for the code. * @param str the string to append. * @param scope a BSON object containing the scope. * * @return BSON_OK or BSON_ERROR. */ int bson_append_code_w_scope( bson *b, const char *name, const char *code, const bson *scope ); /** * Append len bytes of code to a bson with scope. * * @param b the bson to append to. * @param name the key for the code. * @param str the string to append. * @param len the number of bytes from str to append. * @param scope a BSON object containing the scope. * * @return BSON_OK or BSON_ERROR. */ int bson_append_code_w_scope_n( bson *b, const char *name, const char *code, int size, const bson *scope ); /** * Append binary data to a bson. * * @param b the bson to append to. * @param name the key for the data. * @param type the binary data type. * @param str the binary data. * @param len the length of the data. * * @return BSON_OK or BSON_ERROR. */ int bson_append_binary( bson *b, const char *name, char type, const char *str, int len ); /** * Append a bson_bool_t to a bson. * * @param b the bson to append to. * @param name the key for the boolean value. * @param v the bson_bool_t to append. * * @return BSON_OK or BSON_ERROR. */ int bson_append_bool( bson *b, const char *name, const bson_bool_t v ); /** * Append a null value to a bson. * * @param b the bson to append to. * @param name the key for the null value. * * @return BSON_OK or BSON_ERROR. */ int bson_append_null( bson *b, const char *name ); /** * Append an undefined value to a bson. * * @param b the bson to append to. * @param name the key for the undefined value. * * @return BSON_OK or BSON_ERROR. */ int bson_append_undefined( bson *b, const char *name ); /** * Append a regex value to a bson. * * @param b the bson to append to. * @param name the key for the regex value. * @param pattern the regex pattern to append. * @param the regex options. * * @return BSON_OK or BSON_ERROR. */ int bson_append_regex( bson *b, const char *name, const char *pattern, const char *opts ); /** * Append bson data to a bson. * * @param b the bson to append to. * @param name the key for the bson data. * @param bson the bson object to append. * * @return BSON_OK or BSON_ERROR. */ int bson_append_bson( bson *b, const char *name, const bson *bson ); /** * Append a BSON element to a bson from the current point of an iterator. * * @param b the bson to append to. * @param name_or_null the key for the BSON element, or NULL. * @param elem the bson_iterator. * * @return BSON_OK or BSON_ERROR. */ int bson_append_element( bson *b, const char *name_or_null, const bson_iterator *elem ); /** * Append a bson_timestamp_t value to a bson. * * @param b the bson to append to. * @param name the key for the timestampe value. * @param ts the bson_timestamp_t value to append. * * @return BSON_OK or BSON_ERROR. */ int bson_append_timestamp( bson *b, const char *name, bson_timestamp_t *ts ); /* these both append a bson_date */ /** * Append a bson_date_t value to a bson. * * @param b the bson to append to. * @param name the key for the date value. * @param millis the bson_date_t to append. * * @return BSON_OK or BSON_ERROR. */ int bson_append_date( bson *b, const char *name, bson_date_t millis ); /** * Append a time_t value to a bson. * * @param b the bson to append to. * @param name the key for the date value. * @param secs the time_t to append. * * @return BSON_OK or BSON_ERROR. */ int bson_append_time_t( bson *b, const char *name, time_t secs ); /** * Start appending a new object to a bson. * * @param b the bson to append to. * @param name the name of the new object. * * @return BSON_OK or BSON_ERROR. */ int bson_append_start_object( bson *b, const char *name ); /** * Start appending a new array to a bson. * * @param b the bson to append to. * @param name the name of the new array. * * @return BSON_OK or BSON_ERROR. */ int bson_append_start_array( bson *b, const char *name ); /** * Finish appending a new object or array to a bson. * * @param b the bson to append to. * * @return BSON_OK or BSON_ERROR. */ int bson_append_finish_object( bson *b ); /** * Finish appending a new object or array to a bson. This * is simply an alias for bson_append_finish_object. * * @param b the bson to append to. * * @return BSON_OK or BSON_ERROR. */ int bson_append_finish_array( bson *b ); void bson_numstr( char *str, int i ); void bson_incnumstr( char *str ); /* Error handling and stadard library function over-riding. */ /* -------------------------------------------------------- */ /* bson_err_handlers shouldn't return!!! */ typedef void( *bson_err_handler )( const char *errmsg ); typedef int (*bson_printf_func)( const char *, ... ); typedef int (*bson_fprintf_func)( FILE *, const char *, ... ); typedef int (*bson_sprintf_func)( char *, const char *, ... ); extern void *( *bson_malloc_func )( size_t ); extern void *( *bson_realloc_func )( void *, size_t ); extern void ( *bson_free )( void * ); extern bson_printf_func bson_printf; extern bson_fprintf_func bson_fprintf; extern bson_sprintf_func bson_sprintf; extern bson_printf_func bson_errprintf; /** * Allocates memory and checks return value, exiting fatally if malloc() fails. * * @param size bytes to allocate. * * @return a pointer to the allocated memory. * * @sa malloc(3) */ void *bson_malloc( int size ); /** * Changes the size of allocated memory and checks return value, * exiting fatally if realloc() fails. * * @param ptr pointer to the space to reallocate. * @param size bytes to allocate. * * @return a pointer to the allocated memory. * * @sa realloc() */ void *bson_realloc( void *ptr, int size ); /** * Set a function for error handling. * * @param func a bson_err_handler function. * * @return the old error handling function, or NULL. */ bson_err_handler set_bson_err_handler( bson_err_handler func ); /* does nothing if ok != 0 */ /** * Exit fatally. * * @param ok exits if ok is equal to 0. */ void bson_fatal( int ok ); /** * Exit fatally with an error message. * * @param ok exits if ok is equal to 0. * @param msg prints to stderr before exiting. */ void bson_fatal_msg( int ok, const char *msg ); /** * Invoke the error handler, but do not exit. * * @param b the buffer object. */ void bson_builder_error( bson *b ); MONGO_EXTERN_C_END #endif whoopsie-0.2.24.5/lib/bson/bson.c0000664000000000000000000006253412061665776013335 0ustar /* bson.c */ /* Copyright 2009, 2010 10gen Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include "bson.h" #include "encoding.h" const int initialBufferSize = 128; /* only need one of these */ static const int zero = 0; /* Custom standard function pointers. */ void *( *bson_malloc_func )( size_t ) = malloc; void *( *bson_realloc_func )( void *, size_t ) = realloc; void ( *bson_free )( void * ) = free; bson_printf_func bson_printf = printf; bson_fprintf_func bson_fprintf = fprintf; bson_sprintf_func bson_sprintf = sprintf; static int _bson_errprintf( const char *, ... ); bson_printf_func bson_errprintf = _bson_errprintf; /* ObjectId fuzz functions. */ static int ( *oid_fuzz_func )( void ) = NULL; static int ( *oid_inc_func )( void ) = NULL; /* ---------------------------- READING ------------------------------ */ bson *bson_empty( bson *obj ) { static char *data = "\005\0\0\0\0"; bson_init_data( obj, data ); obj->finished = 1; obj->err = 0; obj->stackPos = 0; return obj; } int bson_copy( bson *out, const bson *in ) { if ( !out ) return BSON_ERROR; if ( !in->finished ) return BSON_ERROR; bson_init_size( out, bson_size( in ) ); memcpy( out->data, in->data, bson_size( in ) ); out->finished = 1; return BSON_OK; } int bson_init_data( bson *b, char *data ) { b->data = data; return BSON_OK; } int bson_init_finished_data( bson *b, char *data ) { bson_init_data( b, data ); b->stackPos = 0; b->finished = 1; return BSON_OK; } static void _bson_reset( bson *b ) { b->finished = 0; b->stackPos = 0; b->err = 0; b->errstr = NULL; } int bson_size( const bson *b ) { int i; if ( ! b || ! b->data ) return 0; bson_little_endian32( &i, b->data ); return i; } const char *bson_data( bson *b ) { return (const char *)b->data; } static char hexbyte( char hex ) { switch ( hex ) { case '0': return 0x0; case '1': return 0x1; case '2': return 0x2; case '3': return 0x3; case '4': return 0x4; case '5': return 0x5; case '6': return 0x6; case '7': return 0x7; case '8': return 0x8; case '9': return 0x9; case 'a': case 'A': return 0xa; case 'b': case 'B': return 0xb; case 'c': case 'C': return 0xc; case 'd': case 'D': return 0xd; case 'e': case 'E': return 0xe; case 'f': case 'F': return 0xf; default: return 0x0; /* something smarter? */ } } void bson_oid_from_string( bson_oid_t *oid, const char *str ) { int i; for ( i=0; i<12; i++ ) { oid->bytes[i] = ( hexbyte( str[2*i] ) << 4 ) | hexbyte( str[2*i + 1] ); } } void bson_oid_to_string( const bson_oid_t *oid, char *str ) { static const char hex[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; int i; for ( i=0; i<12; i++ ) { str[2*i] = hex[( oid->bytes[i] & 0xf0 ) >> 4]; str[2*i + 1] = hex[ oid->bytes[i] & 0x0f ]; } str[24] = '\0'; } void bson_set_oid_fuzz( int ( *func )( void ) ) { oid_fuzz_func = func; } void bson_set_oid_inc( int ( *func )( void ) ) { oid_inc_func = func; } void bson_oid_gen( bson_oid_t *oid ) { static int incr = 0; static int fuzz = 0; int i; int t = time( NULL ); if( oid_inc_func ) i = oid_inc_func(); else i = incr++; if ( !fuzz ) { if ( oid_fuzz_func ) fuzz = oid_fuzz_func(); else { srand( t ); fuzz = rand(); } } bson_big_endian32( &oid->ints[0], &t ); oid->ints[1] = fuzz; bson_big_endian32( &oid->ints[2], &i ); } time_t bson_oid_generated_time( bson_oid_t *oid ) { time_t out; bson_big_endian32( &out, &oid->ints[0] ); return out; } void bson_print( bson *b ) { bson_print_raw( b->data , 0 ); } void bson_print_raw( const char *data , int depth ) { bson_iterator i; const char *key; int temp; bson_timestamp_t ts; char oidhex[25]; bson scope; bson_iterator_from_buffer( &i, data ); while ( bson_iterator_next( &i ) ) { bson_type t = bson_iterator_type( &i ); if ( t == 0 ) break; key = bson_iterator_key( &i ); for ( temp=0; temp<=depth; temp++ ) bson_printf( "\t" ); bson_printf( "%s : %d \t " , key , t ); switch ( t ) { case BSON_DOUBLE: bson_printf( "%f" , bson_iterator_double( &i ) ); break; case BSON_STRING: bson_printf( "%s" , bson_iterator_string( &i ) ); break; case BSON_SYMBOL: bson_printf( "SYMBOL: %s" , bson_iterator_string( &i ) ); break; case BSON_OID: bson_oid_to_string( bson_iterator_oid( &i ), oidhex ); bson_printf( "%s" , oidhex ); break; case BSON_BOOL: bson_printf( "%s" , bson_iterator_bool( &i ) ? "true" : "false" ); break; case BSON_DATE: bson_printf( "%ld" , ( long int )bson_iterator_date( &i ) ); break; case BSON_BINDATA: bson_printf( "BSON_BINDATA" ); break; case BSON_UNDEFINED: bson_printf( "BSON_UNDEFINED" ); break; case BSON_NULL: bson_printf( "BSON_NULL" ); break; case BSON_REGEX: bson_printf( "BSON_REGEX: %s", bson_iterator_regex( &i ) ); break; case BSON_CODE: bson_printf( "BSON_CODE: %s", bson_iterator_code( &i ) ); break; case BSON_CODEWSCOPE: bson_printf( "BSON_CODE_W_SCOPE: %s", bson_iterator_code( &i ) ); bson_init( &scope ); bson_iterator_code_scope( &i, &scope ); bson_printf( "\n\t SCOPE: " ); bson_print( &scope ); break; case BSON_INT: bson_printf( "%d" , bson_iterator_int( &i ) ); break; case BSON_LONG: bson_printf( "%lld" , ( uint64_t )bson_iterator_long( &i ) ); break; case BSON_TIMESTAMP: ts = bson_iterator_timestamp( &i ); bson_printf( "i: %d, t: %d", ts.i, ts.t ); break; case BSON_OBJECT: case BSON_ARRAY: bson_printf( "\n" ); bson_print_raw( bson_iterator_value( &i ) , depth + 1 ); break; default: bson_errprintf( "can't print type : %d\n" , t ); } bson_printf( "\n" ); } } /* ---------------------------- ITERATOR ------------------------------ */ void bson_iterator_init( bson_iterator *i, const bson *b ) { i->cur = b->data + 4; i->first = 1; } void bson_iterator_from_buffer( bson_iterator *i, const char *buffer ) { i->cur = buffer + 4; i->first = 1; } bson_type bson_find( bson_iterator *it, const bson *obj, const char *name ) { bson_iterator_init( it, (bson *)obj ); while( bson_iterator_next( it ) ) { if ( strcmp( name, bson_iterator_key( it ) ) == 0 ) break; } return bson_iterator_type( it ); } bson_bool_t bson_iterator_more( const bson_iterator *i ) { return *( i->cur ); } bson_type bson_iterator_next( bson_iterator *i ) { int ds; if ( i->first ) { i->first = 0; return ( bson_type )( *i->cur ); } switch ( bson_iterator_type( i ) ) { case BSON_EOO: return BSON_EOO; /* don't advance */ case BSON_UNDEFINED: case BSON_NULL: ds = 0; break; case BSON_BOOL: ds = 1; break; case BSON_INT: ds = 4; break; case BSON_LONG: case BSON_DOUBLE: case BSON_TIMESTAMP: case BSON_DATE: ds = 8; break; case BSON_OID: ds = 12; break; case BSON_STRING: case BSON_SYMBOL: case BSON_CODE: ds = 4 + bson_iterator_int_raw( i ); break; case BSON_BINDATA: ds = 5 + bson_iterator_int_raw( i ); break; case BSON_OBJECT: case BSON_ARRAY: case BSON_CODEWSCOPE: ds = bson_iterator_int_raw( i ); break; case BSON_DBREF: ds = 4+12 + bson_iterator_int_raw( i ); break; case BSON_REGEX: { const char *s = bson_iterator_value( i ); const char *p = s; p += strlen( p )+1; p += strlen( p )+1; ds = p-s; break; } default: { char msg[] = "unknown type: 000000000000"; bson_numstr( msg+14, ( unsigned )( i->cur[0] ) ); bson_fatal_msg( 0, msg ); return 0; } } i->cur += 1 + strlen( i->cur + 1 ) + 1 + ds; return ( bson_type )( *i->cur ); } bson_type bson_iterator_type( const bson_iterator *i ) { return ( bson_type )i->cur[0]; } const char *bson_iterator_key( const bson_iterator *i ) { return i->cur + 1; } const char *bson_iterator_value( const bson_iterator *i ) { const char *t = i->cur + 1; t += strlen( t ) + 1; return t; } /* types */ int bson_iterator_int_raw( const bson_iterator *i ) { int out; bson_little_endian32( &out, bson_iterator_value( i ) ); return out; } double bson_iterator_double_raw( const bson_iterator *i ) { double out; bson_little_endian64( &out, bson_iterator_value( i ) ); return out; } int64_t bson_iterator_long_raw( const bson_iterator *i ) { int64_t out; bson_little_endian64( &out, bson_iterator_value( i ) ); return out; } bson_bool_t bson_iterator_bool_raw( const bson_iterator *i ) { return bson_iterator_value( i )[0]; } bson_oid_t *bson_iterator_oid( const bson_iterator *i ) { return ( bson_oid_t * )bson_iterator_value( i ); } int bson_iterator_int( const bson_iterator *i ) { switch ( bson_iterator_type( i ) ) { case BSON_INT: return bson_iterator_int_raw( i ); case BSON_LONG: return bson_iterator_long_raw( i ); case BSON_DOUBLE: return bson_iterator_double_raw( i ); default: return 0; } } double bson_iterator_double( const bson_iterator *i ) { switch ( bson_iterator_type( i ) ) { case BSON_INT: return bson_iterator_int_raw( i ); case BSON_LONG: return bson_iterator_long_raw( i ); case BSON_DOUBLE: return bson_iterator_double_raw( i ); default: return 0; } } int64_t bson_iterator_long( const bson_iterator *i ) { switch ( bson_iterator_type( i ) ) { case BSON_INT: return bson_iterator_int_raw( i ); case BSON_LONG: return bson_iterator_long_raw( i ); case BSON_DOUBLE: return bson_iterator_double_raw( i ); default: return 0; } } bson_timestamp_t bson_iterator_timestamp( const bson_iterator *i ) { bson_timestamp_t ts; bson_little_endian32( &( ts.i ), bson_iterator_value( i ) ); bson_little_endian32( &( ts.t ), bson_iterator_value( i ) + 4 ); return ts; } bson_bool_t bson_iterator_bool( const bson_iterator *i ) { switch ( bson_iterator_type( i ) ) { case BSON_BOOL: return bson_iterator_bool_raw( i ); case BSON_INT: return bson_iterator_int_raw( i ) != 0; case BSON_LONG: return bson_iterator_long_raw( i ) != 0; case BSON_DOUBLE: return bson_iterator_double_raw( i ) != 0; case BSON_EOO: case BSON_NULL: return 0; default: return 1; } } const char *bson_iterator_string( const bson_iterator *i ) { return bson_iterator_value( i ) + 4; } int bson_iterator_string_len( const bson_iterator *i ) { return bson_iterator_int_raw( i ); } const char *bson_iterator_code( const bson_iterator *i ) { switch ( bson_iterator_type( i ) ) { case BSON_STRING: case BSON_CODE: return bson_iterator_value( i ) + 4; case BSON_CODEWSCOPE: return bson_iterator_value( i ) + 8; default: return NULL; } } void bson_iterator_code_scope( const bson_iterator *i, bson *scope ) { if ( bson_iterator_type( i ) == BSON_CODEWSCOPE ) { int code_len; bson_little_endian32( &code_len, bson_iterator_value( i )+4 ); bson_init_data( scope, ( void * )( bson_iterator_value( i )+8+code_len ) ); _bson_reset( scope ); scope->finished = 1; } else { bson_empty( scope ); } } bson_date_t bson_iterator_date( const bson_iterator *i ) { return bson_iterator_long_raw( i ); } time_t bson_iterator_time_t( const bson_iterator *i ) { return bson_iterator_date( i ) / 1000; } int bson_iterator_bin_len( const bson_iterator *i ) { return ( bson_iterator_bin_type( i ) == BSON_BIN_BINARY_OLD ) ? bson_iterator_int_raw( i ) - 4 : bson_iterator_int_raw( i ); } char bson_iterator_bin_type( const bson_iterator *i ) { return bson_iterator_value( i )[4]; } const char *bson_iterator_bin_data( const bson_iterator *i ) { return ( bson_iterator_bin_type( i ) == BSON_BIN_BINARY_OLD ) ? bson_iterator_value( i ) + 9 : bson_iterator_value( i ) + 5; } const char *bson_iterator_regex( const bson_iterator *i ) { return bson_iterator_value( i ); } const char *bson_iterator_regex_opts( const bson_iterator *i ) { const char *p = bson_iterator_value( i ); return p + strlen( p ) + 1; } void bson_iterator_subobject( const bson_iterator *i, bson *sub ) { bson_init_data( sub, ( char * )bson_iterator_value( i ) ); _bson_reset( sub ); sub->finished = 1; } void bson_iterator_subiterator( const bson_iterator *i, bson_iterator *sub ) { bson_iterator_from_buffer( sub, bson_iterator_value( i ) ); } /* ---------------------------- BUILDING ------------------------------ */ static void _bson_init_size( bson *b, int size ) { if( size == 0 ) b->data = NULL; else b->data = ( char * )bson_malloc( size ); b->dataSize = size; b->cur = b->data + 4; _bson_reset( b ); } void bson_init( bson *b ) { _bson_init_size( b, initialBufferSize ); } void bson_init_size( bson *b, int size ) { _bson_init_size( b, size ); } void bson_append_byte( bson *b, char c ) { b->cur[0] = c; b->cur++; } void bson_append( bson *b, const void *data, int len ) { memcpy( b->cur , data , len ); b->cur += len; } void bson_append32( bson *b, const void *data ) { bson_little_endian32( b->cur, data ); b->cur += 4; } void bson_append64( bson *b, const void *data ) { bson_little_endian64( b->cur, data ); b->cur += 8; } int bson_ensure_space( bson *b, const int bytesNeeded ) { int pos = b->cur - b->data; char *orig = b->data; int new_size; if ( pos + bytesNeeded <= b->dataSize ) return BSON_OK; new_size = 1.5 * ( b->dataSize + bytesNeeded ); if( new_size < b->dataSize ) { if( ( b->dataSize + bytesNeeded ) < INT_MAX ) new_size = INT_MAX; else { b->err = BSON_SIZE_OVERFLOW; return BSON_ERROR; } } b->data = bson_realloc( b->data, new_size ); if ( !b->data ) bson_fatal_msg( !!b->data, "realloc() failed" ); b->dataSize = new_size; b->cur += b->data - orig; return BSON_OK; } int bson_finish( bson *b ) { int i; if( b->err & BSON_NOT_UTF8 ) return BSON_ERROR; if ( ! b->finished ) { if ( bson_ensure_space( b, 1 ) == BSON_ERROR ) return BSON_ERROR; bson_append_byte( b, 0 ); i = b->cur - b->data; bson_little_endian32( b->data, &i ); b->finished = 1; } return BSON_OK; } void bson_destroy( bson *b ) { bson_free( b->data ); b->err = 0; b->data = 0; b->cur = 0; b->finished = 1; } static int bson_append_estart( bson *b, int type, const char *name, const int dataSize ) { const int len = strlen( name ) + 1; if ( b->finished ) { b->err |= BSON_ALREADY_FINISHED; return BSON_ERROR; } if ( bson_ensure_space( b, 1 + len + dataSize ) == BSON_ERROR ) { return BSON_ERROR; } if( bson_check_field_name( b, ( const char * )name, len - 1 ) == BSON_ERROR ) { bson_builder_error( b ); return BSON_ERROR; } bson_append_byte( b, ( char )type ); bson_append( b, name, len ); return BSON_OK; } /* ---------------------------- BUILDING TYPES ------------------------------ */ int bson_append_int( bson *b, const char *name, const int i ) { if ( bson_append_estart( b, BSON_INT, name, 4 ) == BSON_ERROR ) return BSON_ERROR; bson_append32( b , &i ); return BSON_OK; } int bson_append_long( bson *b, const char *name, const int64_t i ) { if ( bson_append_estart( b , BSON_LONG, name, 8 ) == BSON_ERROR ) return BSON_ERROR; bson_append64( b , &i ); return BSON_OK; } int bson_append_double( bson *b, const char *name, const double d ) { if ( bson_append_estart( b, BSON_DOUBLE, name, 8 ) == BSON_ERROR ) return BSON_ERROR; bson_append64( b , &d ); return BSON_OK; } int bson_append_bool( bson *b, const char *name, const bson_bool_t i ) { if ( bson_append_estart( b, BSON_BOOL, name, 1 ) == BSON_ERROR ) return BSON_ERROR; bson_append_byte( b , i != 0 ); return BSON_OK; } int bson_append_null( bson *b, const char *name ) { if ( bson_append_estart( b , BSON_NULL, name, 0 ) == BSON_ERROR ) return BSON_ERROR; return BSON_OK; } int bson_append_undefined( bson *b, const char *name ) { if ( bson_append_estart( b, BSON_UNDEFINED, name, 0 ) == BSON_ERROR ) return BSON_ERROR; return BSON_OK; } int bson_append_string_base( bson *b, const char *name, const char *value, int len, bson_type type ) { int sl = len + 1; if ( bson_check_string( b, ( const char * )value, sl - 1 ) == BSON_ERROR ) return BSON_ERROR; if ( bson_append_estart( b, type, name, 4 + sl ) == BSON_ERROR ) { return BSON_ERROR; } bson_append32( b , &sl ); bson_append( b , value , sl - 1 ); bson_append( b , "\0" , 1 ); return BSON_OK; } int bson_append_string( bson *b, const char *name, const char *value ) { return bson_append_string_base( b, name, value, strlen ( value ), BSON_STRING ); } int bson_append_symbol( bson *b, const char *name, const char *value ) { return bson_append_string_base( b, name, value, strlen ( value ), BSON_SYMBOL ); } int bson_append_code( bson *b, const char *name, const char *value ) { return bson_append_string_base( b, name, value, strlen ( value ), BSON_CODE ); } int bson_append_string_n( bson *b, const char *name, const char *value, int len ) { return bson_append_string_base( b, name, value, len, BSON_STRING ); } int bson_append_symbol_n( bson *b, const char *name, const char *value, int len ) { return bson_append_string_base( b, name, value, len, BSON_SYMBOL ); } int bson_append_code_n( bson *b, const char *name, const char *value, int len ) { return bson_append_string_base( b, name, value, len, BSON_CODE ); } int bson_append_code_w_scope_n( bson *b, const char *name, const char *code, int len, const bson *scope ) { int sl = len + 1; int size = 4 + 4 + sl + bson_size( scope ); if ( bson_append_estart( b, BSON_CODEWSCOPE, name, size ) == BSON_ERROR ) return BSON_ERROR; bson_append32( b, &size ); bson_append32( b, &sl ); bson_append( b, code, sl ); bson_append( b, scope->data, bson_size( scope ) ); return BSON_OK; } int bson_append_code_w_scope( bson *b, const char *name, const char *code, const bson *scope ) { return bson_append_code_w_scope_n( b, name, code, strlen ( code ), scope ); } int bson_append_binary( bson *b, const char *name, char type, const char *str, int len ) { if ( type == BSON_BIN_BINARY_OLD ) { int subtwolen = len + 4; if ( bson_append_estart( b, BSON_BINDATA, name, 4+1+4+len ) == BSON_ERROR ) return BSON_ERROR; bson_append32( b, &subtwolen ); bson_append_byte( b, type ); bson_append32( b, &len ); bson_append( b, str, len ); } else { if ( bson_append_estart( b, BSON_BINDATA, name, 4+1+len ) == BSON_ERROR ) return BSON_ERROR; bson_append32( b, &len ); bson_append_byte( b, type ); bson_append( b, str, len ); } return BSON_OK; } int bson_append_oid( bson *b, const char *name, const bson_oid_t *oid ) { if ( bson_append_estart( b, BSON_OID, name, 12 ) == BSON_ERROR ) return BSON_ERROR; bson_append( b , oid , 12 ); return BSON_OK; } int bson_append_new_oid( bson *b, const char *name ) { bson_oid_t oid; bson_oid_gen( &oid ); return bson_append_oid( b, name, &oid ); } int bson_append_regex( bson *b, const char *name, const char *pattern, const char *opts ) { const int plen = strlen( pattern )+1; const int olen = strlen( opts )+1; if ( bson_append_estart( b, BSON_REGEX, name, plen + olen ) == BSON_ERROR ) return BSON_ERROR; if ( bson_check_string( b, pattern, plen - 1 ) == BSON_ERROR ) return BSON_ERROR; bson_append( b , pattern , plen ); bson_append( b , opts , olen ); return BSON_OK; } int bson_append_bson( bson *b, const char *name, const bson *bson ) { if ( bson_append_estart( b, BSON_OBJECT, name, bson_size( bson ) ) == BSON_ERROR ) return BSON_ERROR; bson_append( b , bson->data , bson_size( bson ) ); return BSON_OK; } int bson_append_element( bson *b, const char *name_or_null, const bson_iterator *elem ) { bson_iterator next = *elem; int size; bson_iterator_next( &next ); size = next.cur - elem->cur; if ( name_or_null == NULL ) { if( bson_ensure_space( b, size ) == BSON_ERROR ) return BSON_ERROR; bson_append( b, elem->cur, size ); } else { int data_size = size - 2 - strlen( bson_iterator_key( elem ) ); bson_append_estart( b, elem->cur[0], name_or_null, data_size ); bson_append( b, bson_iterator_value( elem ), data_size ); } return BSON_OK; } int bson_append_timestamp( bson *b, const char *name, bson_timestamp_t *ts ) { if ( bson_append_estart( b, BSON_TIMESTAMP, name, 8 ) == BSON_ERROR ) return BSON_ERROR; bson_append32( b , &( ts->i ) ); bson_append32( b , &( ts->t ) ); return BSON_OK; } int bson_append_date( bson *b, const char *name, bson_date_t millis ) { if ( bson_append_estart( b, BSON_DATE, name, 8 ) == BSON_ERROR ) return BSON_ERROR; bson_append64( b , &millis ); return BSON_OK; } int bson_append_time_t( bson *b, const char *name, time_t secs ) { return bson_append_date( b, name, ( bson_date_t )secs * 1000 ); } int bson_append_start_object( bson *b, const char *name ) { if ( bson_append_estart( b, BSON_OBJECT, name, 5 ) == BSON_ERROR ) return BSON_ERROR; b->stack[ b->stackPos++ ] = b->cur - b->data; bson_append32( b , &zero ); return BSON_OK; } int bson_append_start_array( bson *b, const char *name ) { if ( bson_append_estart( b, BSON_ARRAY, name, 5 ) == BSON_ERROR ) return BSON_ERROR; b->stack[ b->stackPos++ ] = b->cur - b->data; bson_append32( b , &zero ); return BSON_OK; } int bson_append_finish_object( bson *b ) { char *start; int i; if ( bson_ensure_space( b, 1 ) == BSON_ERROR ) return BSON_ERROR; bson_append_byte( b , 0 ); start = b->data + b->stack[ --b->stackPos ]; i = b->cur - start; bson_little_endian32( start, &i ); return BSON_OK; } int bson_append_finish_array( bson *b ) { return bson_append_finish_object( b ); } /* Error handling and allocators. */ static bson_err_handler err_handler = NULL; bson_err_handler set_bson_err_handler( bson_err_handler func ) { bson_err_handler old = err_handler; err_handler = func; return old; } void *bson_malloc( int size ) { void *p; p = bson_malloc_func( size ); bson_fatal_msg( !!p, "malloc() failed" ); return p; } void *bson_realloc( void *ptr, int size ) { void *p; p = bson_realloc_func( ptr, size ); bson_fatal_msg( !!p, "realloc() failed" ); return p; } int _bson_errprintf( const char *format, ... ) { va_list ap; int ret; va_start( ap, format ); ret = vfprintf( stderr, format, ap ); va_end( ap ); return ret; } /** * This method is invoked when a non-fatal bson error is encountered. * Calls the error handler if available. * * @param */ void bson_builder_error( bson *b ) { if( err_handler ) err_handler( "BSON error." ); } void bson_fatal( int ok ) { bson_fatal_msg( ok, "" ); } void bson_fatal_msg( int ok , const char *msg ) { if ( ok ) return; if ( err_handler ) { err_handler( msg ); } bson_errprintf( "error: %s\n" , msg ); exit( -5 ); } /* Efficiently copy an integer to a string. */ extern const char bson_numstrs[1000][4]; void bson_numstr( char *str, int i ) { if( i < 1000 ) memcpy( str, bson_numstrs[i], 4 ); else bson_sprintf( str,"%d", i ); } whoopsie-0.2.24.5/lib/bson/encoding.h0000664000000000000000000000360612061665776014162 0ustar /* * Copyright 2009-2011 10gen, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef _BSON_ENCODING_H_ #define _BSON_ENCODING_H_ MONGO_EXTERN_C_START /** * Check that a field name is valid UTF8, does not start with a '$', * and contains no '.' characters. Set bson bit field appropriately. * Note that we don't need to check for '\0' because we're using * strlen(3), which stops at '\0'. * * @param b The bson object to which field name will be appended. * @param string The field name as char*. * @param length The length of the field name. * * @return BSON_OK if valid UTF8 and BSON_ERROR if not. All BSON strings must be * valid UTF8. This function will also check whether the string * contains '.' or starts with '$', since the validity of this depends on context. * Set the value of b->err appropriately. */ int bson_check_field_name( bson *b, const char *string, const int length ); /** * Check that a string is valid UTF8. Sets the buffer bit field appropriately. * * @param b The bson object to which string will be appended. * @param string The string to check. * @param length The length of the string. * * @return BSON_OK if valid UTF-8; otherwise, BSON_ERROR. * Sets b->err on error. */ bson_bool_t bson_check_string( bson *b, const char *string, const int length ); MONGO_EXTERN_C_END #endif whoopsie-0.2.24.5/lib/bson/encoding.c0000664000000000000000000001203012061665776014144 0ustar /* * Copyright 2009-2011 10gen, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Portions Copyright 2001 Unicode, Inc. * * Disclaimer * * This source code is provided as is by Unicode, Inc. No claims are * made as to fitness for any particular purpose. No warranties of any * kind are expressed or implied. The recipient agrees to determine * applicability of information provided. If this file has been * purchased on magnetic or optical media from Unicode, Inc., the * sole remedy for any claim will be exchange of defective media * within 90 days of receipt. * * Limitations on Rights to Redistribute This Code * * Unicode, Inc. hereby grants the right to freely use the information * supplied in this file in the creation of products supporting the * Unicode Standard, and to make copies of this file in any form * for internal or external distribution as long as this notice * remains attached. */ #include "bson.h" #include "encoding.h" /* * Index into the table below with the first byte of a UTF-8 sequence to * get the number of trailing bytes that are supposed to follow it. */ static const char trailingBytesForUTF8[256] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 }; /* --------------------------------------------------------------------- */ /* * Utility routine to tell whether a sequence of bytes is legal UTF-8. * This must be called with the length pre-determined by the first byte. * The length can be set by: * length = trailingBytesForUTF8[*source]+1; * and the sequence is illegal right away if there aren't that many bytes * available. * If presented with a length > 4, this returns 0. The Unicode * definition of UTF-8 goes up to 4-byte sequences. */ static int isLegalUTF8( const unsigned char *source, int length ) { unsigned char a; const unsigned char *srcptr = source + length; switch ( length ) { default: return 0; /* Everything else falls through when "true"... */ case 4: if ( ( a = ( *--srcptr ) ) < 0x80 || a > 0xBF ) return 0; case 3: if ( ( a = ( *--srcptr ) ) < 0x80 || a > 0xBF ) return 0; case 2: if ( ( a = ( *--srcptr ) ) > 0xBF ) return 0; switch ( *source ) { /* no fall-through in this inner switch */ case 0xE0: if ( a < 0xA0 ) return 0; break; case 0xF0: if ( a < 0x90 ) return 0; break; case 0xF4: if ( a > 0x8F ) return 0; break; default: if ( a < 0x80 ) return 0; } case 1: if ( *source >= 0x80 && *source < 0xC2 ) return 0; if ( *source > 0xF4 ) return 0; } return 1; } static int bson_validate_string( bson *b, const unsigned char *string, const int length, const char check_utf8, const char check_dot, const char check_dollar ) { int position = 0; int sequence_length = 1; if( check_dollar && string[0] == '$' ) { b->err |= BSON_FIELD_INIT_DOLLAR; } while ( position < length ) { if ( check_dot && *( string + position ) == '.' ) { b->err |= BSON_FIELD_HAS_DOT; } if ( check_utf8 ) { sequence_length = trailingBytesForUTF8[*( string + position )] + 1; if ( ( position + sequence_length ) > length ) { b->err |= BSON_NOT_UTF8; return BSON_ERROR; } if ( !isLegalUTF8( string + position, sequence_length ) ) { b->err |= BSON_NOT_UTF8; return BSON_ERROR; } } position += sequence_length; } return BSON_OK; } int bson_check_string( bson *b, const char *string, const int length ) { return bson_validate_string( b, ( const unsigned char * )string, length, 1, 0, 0 ); } int bson_check_field_name( bson *b, const char *string, const int length ) { return bson_validate_string( b, ( const unsigned char * )string, length, 1, 1, 1 ); } whoopsie-0.2.24.5/lib/bson/numbers.c0000664000000000000000000001752512061665776014047 0ustar /* Copyright 2009-2011 10gen Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* all the numbers that fit in a 4 byte string */ const char bson_numstrs[1000][4] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "100", "101", "102", "103", "104", "105", "106", "107", "108", "109", "110", "111", "112", "113", "114", "115", "116", "117", "118", "119", "120", "121", "122", "123", "124", "125", "126", "127", "128", "129", "130", "131", "132", "133", "134", "135", "136", "137", "138", "139", "140", "141", "142", "143", "144", "145", "146", "147", "148", "149", "150", "151", "152", "153", "154", "155", "156", "157", "158", "159", "160", "161", "162", "163", "164", "165", "166", "167", "168", "169", "170", "171", "172", "173", "174", "175", "176", "177", "178", "179", "180", "181", "182", "183", "184", "185", "186", "187", "188", "189", "190", "191", "192", "193", "194", "195", "196", "197", "198", "199", "200", "201", "202", "203", "204", "205", "206", "207", "208", "209", "210", "211", "212", "213", "214", "215", "216", "217", "218", "219", "220", "221", "222", "223", "224", "225", "226", "227", "228", "229", "230", "231", "232", "233", "234", "235", "236", "237", "238", "239", "240", "241", "242", "243", "244", "245", "246", "247", "248", "249", "250", "251", "252", "253", "254", "255", "256", "257", "258", "259", "260", "261", "262", "263", "264", "265", "266", "267", "268", "269", "270", "271", "272", "273", "274", "275", "276", "277", "278", "279", "280", "281", "282", "283", "284", "285", "286", "287", "288", "289", "290", "291", "292", "293", "294", "295", "296", "297", "298", "299", "300", "301", "302", "303", "304", "305", "306", "307", "308", "309", "310", "311", "312", "313", "314", "315", "316", "317", "318", "319", "320", "321", "322", "323", "324", "325", "326", "327", "328", "329", "330", "331", "332", "333", "334", "335", "336", "337", "338", "339", "340", "341", "342", "343", "344", "345", "346", "347", "348", "349", "350", "351", "352", "353", "354", "355", "356", "357", "358", "359", "360", "361", "362", "363", "364", "365", "366", "367", "368", "369", "370", "371", "372", "373", "374", "375", "376", "377", "378", "379", "380", "381", "382", "383", "384", "385", "386", "387", "388", "389", "390", "391", "392", "393", "394", "395", "396", "397", "398", "399", "400", "401", "402", "403", "404", "405", "406", "407", "408", "409", "410", "411", "412", "413", "414", "415", "416", "417", "418", "419", "420", "421", "422", "423", "424", "425", "426", "427", "428", "429", "430", "431", "432", "433", "434", "435", "436", "437", "438", "439", "440", "441", "442", "443", "444", "445", "446", "447", "448", "449", "450", "451", "452", "453", "454", "455", "456", "457", "458", "459", "460", "461", "462", "463", "464", "465", "466", "467", "468", "469", "470", "471", "472", "473", "474", "475", "476", "477", "478", "479", "480", "481", "482", "483", "484", "485", "486", "487", "488", "489", "490", "491", "492", "493", "494", "495", "496", "497", "498", "499", "500", "501", "502", "503", "504", "505", "506", "507", "508", "509", "510", "511", "512", "513", "514", "515", "516", "517", "518", "519", "520", "521", "522", "523", "524", "525", "526", "527", "528", "529", "530", "531", "532", "533", "534", "535", "536", "537", "538", "539", "540", "541", "542", "543", "544", "545", "546", "547", "548", "549", "550", "551", "552", "553", "554", "555", "556", "557", "558", "559", "560", "561", "562", "563", "564", "565", "566", "567", "568", "569", "570", "571", "572", "573", "574", "575", "576", "577", "578", "579", "580", "581", "582", "583", "584", "585", "586", "587", "588", "589", "590", "591", "592", "593", "594", "595", "596", "597", "598", "599", "600", "601", "602", "603", "604", "605", "606", "607", "608", "609", "610", "611", "612", "613", "614", "615", "616", "617", "618", "619", "620", "621", "622", "623", "624", "625", "626", "627", "628", "629", "630", "631", "632", "633", "634", "635", "636", "637", "638", "639", "640", "641", "642", "643", "644", "645", "646", "647", "648", "649", "650", "651", "652", "653", "654", "655", "656", "657", "658", "659", "660", "661", "662", "663", "664", "665", "666", "667", "668", "669", "670", "671", "672", "673", "674", "675", "676", "677", "678", "679", "680", "681", "682", "683", "684", "685", "686", "687", "688", "689", "690", "691", "692", "693", "694", "695", "696", "697", "698", "699", "700", "701", "702", "703", "704", "705", "706", "707", "708", "709", "710", "711", "712", "713", "714", "715", "716", "717", "718", "719", "720", "721", "722", "723", "724", "725", "726", "727", "728", "729", "730", "731", "732", "733", "734", "735", "736", "737", "738", "739", "740", "741", "742", "743", "744", "745", "746", "747", "748", "749", "750", "751", "752", "753", "754", "755", "756", "757", "758", "759", "760", "761", "762", "763", "764", "765", "766", "767", "768", "769", "770", "771", "772", "773", "774", "775", "776", "777", "778", "779", "780", "781", "782", "783", "784", "785", "786", "787", "788", "789", "790", "791", "792", "793", "794", "795", "796", "797", "798", "799", "800", "801", "802", "803", "804", "805", "806", "807", "808", "809", "810", "811", "812", "813", "814", "815", "816", "817", "818", "819", "820", "821", "822", "823", "824", "825", "826", "827", "828", "829", "830", "831", "832", "833", "834", "835", "836", "837", "838", "839", "840", "841", "842", "843", "844", "845", "846", "847", "848", "849", "850", "851", "852", "853", "854", "855", "856", "857", "858", "859", "860", "861", "862", "863", "864", "865", "866", "867", "868", "869", "870", "871", "872", "873", "874", "875", "876", "877", "878", "879", "880", "881", "882", "883", "884", "885", "886", "887", "888", "889", "890", "891", "892", "893", "894", "895", "896", "897", "898", "899", "900", "901", "902", "903", "904", "905", "906", "907", "908", "909", "910", "911", "912", "913", "914", "915", "916", "917", "918", "919", "920", "921", "922", "923", "924", "925", "926", "927", "928", "929", "930", "931", "932", "933", "934", "935", "936", "937", "938", "939", "940", "941", "942", "943", "944", "945", "946", "947", "948", "949", "950", "951", "952", "953", "954", "955", "956", "957", "958", "959", "960", "961", "962", "963", "964", "965", "966", "967", "968", "969", "970", "971", "972", "973", "974", "975", "976", "977", "978", "979", "980", "981", "982", "983", "984", "985", "986", "987", "988", "989", "990", "991", "992", "993", "994", "995", "996", "997", "998", "999", }; whoopsie-0.2.24.5/src/0000775000000000000000000000000012321555753011266 5ustar whoopsie-0.2.24.5/src/connectivity.h0000664000000000000000000000173212220367022014144 0ustar /* whoopsie * * Copyright © 2011-2013 Canonical Ltd. * Author: Evan Dandrea * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef CONNECTIVITY_H #define CONNECTIVITY_H typedef void (*ConnectionAvailableCallback) (gboolean online); gboolean monitor_connectivity (const char* crash_url, ConnectionAvailableCallback callback); void unmonitor_connectivity (void); #endif /* CONNECTIVITY_H */ whoopsie-0.2.24.5/src/logging.c0000664000000000000000000000254112267517325013064 0ustar /* whoopsie * * Copyright © 2011-2013 Canonical Ltd. * Author: Evan Dandrea * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #define _BSD_SOURCE #include #include #include #include "globals.h" static int logging_started = 0; int logging_flags = LOG_PID; void log_msg (const char* fmt, ...) { va_list argp; va_start (argp, fmt); if (foreground || !logging_started) { vfprintf (stdout, fmt, argp); fflush (stdout); } else { vsyslog(LOG_INFO, fmt, argp); } va_end (argp); } void open_log (void) { logging_started = 1; openlog ("whoopsie", logging_flags, LOG_DAEMON); } void close_log (void) { if (!foreground && logging_started) { logging_started = 0; closelog (); } } whoopsie-0.2.24.5/src/whoopsie.h0000664000000000000000000000214712220367022013264 0ustar /* whoopsie * * Copyright © 2011-2013 Canonical Ltd. * Author: Evan Dandrea * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef WHOOPSIE_H #define WHOOPSIE_H GHashTable* parse_report (const char* report_path, gboolean full_report, GError** error); void get_system_uuid (char* res, GError** error); char* get_crash_db_url (void); void destroy_key_and_value (gpointer key, gpointer value, gpointer user_data); void drop_privileges (GError** error); gboolean process_existing_files (const char* report_dir); #endif /* WHOOPSIE_H */ whoopsie-0.2.24.5/src/connectivity.c0000664000000000000000000003313712321554251014147 0ustar /* whoopsie * * Copyright © 2011-2013 Canonical Ltd. * Author: Evan Dandrea * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #define _XOPEN_SOURCE #include #include #include #include #include "connectivity.h" #include "logging.h" #define NETWORK_MANAGER_SERVICE "org.freedesktop.NetworkManager" #define NETWORK_MANAGER_OBJECT "/org/freedesktop/NetworkManager" #define NETWORK_MANAGER_INTERFACE "org.freedesktop.NetworkManager" #define NETWORK_MANAGER_ACTIVE_INTERFACE "org.freedesktop.NetworkManager.Connection.Active" #define NETWORK_MANAGER_DEVICE_INTERFACE "org.freedesktop.NetworkManager.Device" #define DBUS_PROPERTIES_INTERFACE "org.freedesktop.DBus.Properties" static gboolean route_available = FALSE; static gboolean network_available = FALSE; struct _connectivity_data { ConnectionAvailableCallback callback; const char* url; }; static struct _connectivity_data connectivity_data; static int subscription_id = 0; GDBusConnection* system_bus = NULL; gboolean is_default_route (GDBusConnection* connection, const gchar* path, gboolean v6) { /* Whether this active connection is the default IPv4 or IPv6 connection. */ GVariant* properties = NULL; GVariant* result = NULL; gboolean value; GError* err = NULL; g_return_val_if_fail (connection, FALSE); g_return_val_if_fail (path, FALSE); result = g_dbus_connection_call_sync (connection, NETWORK_MANAGER_SERVICE, path, DBUS_PROPERTIES_INTERFACE, "Get", g_variant_new ("(ss)", NETWORK_MANAGER_ACTIVE_INTERFACE, v6 ? "Default6" : "Default"), G_VARIANT_TYPE ("(v)"), G_DBUS_CALL_FLAGS_NONE, 3000, NULL, &err); if (!result) { log_msg ("Could not determine if this is a default route:\n"); if (err) { log_msg ("%s\n", err->message); g_error_free (err); } return FALSE; } g_variant_get (result, "(v)", &properties); value = g_variant_get_boolean (properties); g_variant_unref (result); g_variant_unref (properties); return value; } gboolean device_is_paid_data_plan (GDBusConnection* connection, const gchar* path) { /* Whether this device is a type often associated with paid data plans */ GError* err = NULL; GVariant* result = NULL; GVariant* properties = NULL; guint device_type; g_return_val_if_fail (connection, FALSE); g_return_val_if_fail (path, FALSE); result = g_dbus_connection_call_sync (connection, NETWORK_MANAGER_SERVICE, path, DBUS_PROPERTIES_INTERFACE, "Get", g_variant_new ("(ss)", NETWORK_MANAGER_DEVICE_INTERFACE, "DeviceType"), G_VARIANT_TYPE ("(v)"), G_DBUS_CALL_FLAGS_NONE, 3000, NULL, &err); if (!result) { log_msg ("Could not get the device type of %s: %s\n", path, err->message); return FALSE; } g_variant_get (result, "(v)", &properties); device_type = g_variant_get_uint32 (properties); g_variant_unref (result); g_variant_unref (properties); /* We're on a connection that is potentially billed for the data * used (3G, dial-up modem, WIMAX). Bail out. */ if (device_type > NM_DEVICE_TYPE_WIFI) { return TRUE; } return FALSE; } gboolean is_paid_data_plan (GDBusConnection* connection, const gchar* path) { GError* err = NULL; GVariant* result = NULL; GVariant* properties = NULL; GVariantIter* iter = NULL; gchar* device_path; g_return_val_if_fail (connection, FALSE); g_return_val_if_fail (path, FALSE); result = g_dbus_connection_call_sync (connection, NETWORK_MANAGER_SERVICE, path, DBUS_PROPERTIES_INTERFACE, "Get", g_variant_new ("(ss)", NETWORK_MANAGER_ACTIVE_INTERFACE, "Devices"), G_VARIANT_TYPE ("(v)"), G_DBUS_CALL_FLAGS_NONE, 3000, NULL, &err); if (!result) { log_msg ("Could not get the devices for %s: %s\n", path, err->message); return FALSE; } g_variant_get (result, "(v)", &properties); g_variant_get (properties, "ao", &iter); while (g_variant_iter_loop (iter, "&o", &device_path)) { if (device_is_paid_data_plan (connection, device_path)) { g_variant_iter_free (iter); g_variant_unref (result); g_variant_unref (properties); return TRUE; } } g_variant_iter_free (iter); g_variant_unref (result); g_variant_unref (properties); return FALSE; } void network_manager_state_changed (GDBusConnection* connection, const gchar* sender_name, const gchar* object_path, const gchar* interface_name, const gchar* signal_name, GVariant* parameters, gpointer user_data) { GVariant* result = NULL; GVariant* properties = NULL; GVariantIter* iter = NULL; GError* err = NULL; gchar* path = NULL; guint32 connected_state; gboolean paid = TRUE; ConnectionAvailableCallback callback = user_data; g_return_if_fail (connection); g_return_if_fail (parameters); g_variant_get_child (parameters, 0, "u", &connected_state); if (NM_STATE_CONNECTED_GLOBAL != connected_state) { network_available = FALSE; callback (network_available); return; } result = g_dbus_connection_call_sync (connection, NETWORK_MANAGER_SERVICE, NETWORK_MANAGER_OBJECT, DBUS_PROPERTIES_INTERFACE, "Get", g_variant_new ("(ss)", NETWORK_MANAGER_INTERFACE, "ActiveConnections"), G_VARIANT_TYPE ("(v)"), G_DBUS_CALL_FLAGS_NONE, 3000, NULL, &err); if (!result) { log_msg ("Could not get the list of active connections: %s\n", err->message); return; } g_variant_get (result, "(v)", &properties); g_variant_get (properties, "ao", &iter); while (g_variant_iter_loop (iter, "&o", &path)) { if (is_default_route (connection, path, FALSE) || is_default_route (connection, path, TRUE)) { if (!is_paid_data_plan (connection, path)) { paid = FALSE; break; } } } g_variant_iter_free (iter); g_variant_unref (result); g_variant_unref (properties); network_available = !paid; callback (network_available && route_available); } void route_changed (GNetworkMonitor *nm, gboolean available, gpointer user_data) { GError* err = NULL; GSocketConnectable *addr = NULL; g_return_if_fail (nm); if (!available) { route_available = FALSE; return; } addr = g_network_address_parse_uri (connectivity_data.url, 80, &err); if (!addr) { log_msg ("Could not parse crash database URL:\n"); if (err) { log_msg ("%s\n", err->message); g_error_free (err); } return; } route_available = g_network_monitor_can_reach (nm, addr, NULL, NULL); connectivity_data.callback (network_available && route_available); g_object_unref (addr); } void setup_network_route_monitor (void) { GNetworkMonitor* nm = NULL; GSocketConnectable *addr = NULL; GError* err = NULL; /* Using GNetworkMonitor brings in GSettings, which brings in DConf, which * brings in a DBus session bus, which brings in pain. */ if (putenv ("GSETTINGS_BACKEND=memory") != 0) log_msg ("Could not set the GSettings backend to memory.\n"); addr = g_network_address_parse_uri (connectivity_data.url, 80, &err); if (!addr) { log_msg ("Could not parse crash database URL:\n"); if (err) { log_msg ("%s\n", err->message); g_error_free (err); } return; } nm = g_network_monitor_get_default (); if (!nm) { log_msg ("Could not get the network monitor.\n"); goto out; } route_available = (g_network_monitor_get_network_available (nm) && g_network_monitor_can_reach (nm, addr, NULL, NULL)); g_signal_connect (nm, "network-changed", G_CALLBACK (route_changed), NULL); out: if (addr) g_object_unref (addr); } gboolean monitor_connectivity (const char* crash_url, ConnectionAvailableCallback callback) { GError* err = NULL; GVariant* result = NULL; GVariant* properties = NULL; GVariant* current_state = NULL; guint value = 0; g_assert (subscription_id == 0); g_return_val_if_fail (crash_url, FALSE); connectivity_data.url = crash_url; connectivity_data.callback = callback; /* Checking whether a NetworkManager connection is not enough. * NetworkManager will report CONNECTED_GLOBAL when a route is not present * when the connectivity option is not set. We'll use GNetworkMonitor here * to fill in the gap. */ setup_network_route_monitor (); system_bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &err); if (err) { log_msg ("Could not connect to the system bus: %s\n", err->message); g_error_free (err); return FALSE; } subscription_id = g_dbus_connection_signal_subscribe (system_bus, NETWORK_MANAGER_SERVICE, NETWORK_MANAGER_INTERFACE, "StateChanged", NULL, NULL, (GDBusSignalFlags) NULL, network_manager_state_changed, callback, NULL); if (!subscription_id) { log_msg ("Could not subscribe to StateChanged signal.\n"); return FALSE; } result = g_dbus_connection_call_sync (system_bus, NETWORK_MANAGER_SERVICE, NETWORK_MANAGER_OBJECT, DBUS_PROPERTIES_INTERFACE, "Get", g_variant_new ("(ss)", NETWORK_MANAGER_INTERFACE, "state"), G_VARIANT_TYPE ("(v)"), G_DBUS_CALL_FLAGS_NONE, 3000, NULL, &err); if (!result) { log_msg ("Could not get the Network Manager state:\n"); if (err) { log_msg ("%s\n", err->message); g_error_free (err); } return TRUE; } g_variant_get (result, "(v)", &properties); value = g_variant_get_uint32 (properties); current_state = g_variant_new ("(u)", value); network_manager_state_changed (system_bus, NULL, NULL, NULL, NULL, current_state, callback); g_variant_unref (current_state); g_variant_unref (result); g_variant_unref (properties); return TRUE; } void unmonitor_connectivity (void) { GError* err = NULL; if (err) { log_msg ("Could not unmonitor: %s\n", err->message); g_error_free (err); return; } if (system_bus) { g_dbus_connection_signal_unsubscribe (system_bus, subscription_id); g_object_unref (system_bus); } } whoopsie-0.2.24.5/src/identifier.c0000664000000000000000000001141312220367022013540 0ustar #include #include #include #include #include #include #include #include #include #include #include "identifier.h" void whoopsie_hex_to_char (char* buf, const char *str, int len) { char* p = NULL; int i = 0; g_return_if_fail (buf); g_return_if_fail (str); p = buf; for (i = 0; i < len; i++) { snprintf(p, 3, "%02x", (unsigned char) str[i]); p += 2; } buf[2*len] = 0; } void whoopsie_identifier_get_mac_address (char** res, GError** error) { struct ifreq ifr; struct ifconf ifc; char buf[1024]; gboolean success = FALSE; int sock; struct ifreq* it; int i = 0; g_return_if_fail (res); sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); if (sock == -1) { g_set_error (error, g_quark_from_static_string ("whoopsie-quark"), 0, "Could not create socket."); return; } ifc.ifc_len = sizeof(buf); ifc.ifc_buf = buf; if (ioctl(sock, SIOCGIFCONF, &ifc) == -1) { g_set_error (error, g_quark_from_static_string ("whoopsie-quark"), 0, "Could not get list of interface addresses."); close (sock); return; } it = ifc.ifc_req; for (i = ifc.ifc_len / sizeof(struct ifreq); --i >= 0; it++) { strcpy(ifr.ifr_name, it->ifr_name); if (ioctl(sock, SIOCGIFFLAGS, &ifr) == 0) { if (! (ifr.ifr_flags & IFF_LOOPBACK)) { // don't count loopback if (ioctl(sock, SIOCGIFHWADDR, &ifr) == 0) { success = TRUE; break; } } } } close (sock); if (!success) return; *res = g_malloc (18); sprintf (*res, "%02x:%02x:%02x:%02x:%02x:%02x", (unsigned char) ifr.ifr_hwaddr.sa_data[0], (unsigned char) ifr.ifr_hwaddr.sa_data[1], (unsigned char) ifr.ifr_hwaddr.sa_data[2], (unsigned char) ifr.ifr_hwaddr.sa_data[3], (unsigned char) ifr.ifr_hwaddr.sa_data[4], (unsigned char) ifr.ifr_hwaddr.sa_data[5]); } void whoopsie_identifier_get_system_uuid (char** res, GError** error) { int fp; g_return_if_fail (res); fp = open ("/sys/class/dmi/id/product_uuid", O_RDONLY); if (fp < 0) { g_set_error (error, g_quark_from_static_string ("whoopsie-quark"), 0, "Could not open the product uuid file."); return; } *res = g_malloc (37); if (read (fp, *res, 36) == 36) (*res)[36] = '\0'; else g_set_error (error, g_quark_from_static_string ("whoopsie-quark"), 0, "Got an unexpected length reading the product_uuid."); close (fp); } void whoopsie_identifier_sha512 (char* source, char* res, GError** error) { int md_len; gcry_md_hd_t sha512 = NULL; unsigned char* id = NULL; g_return_if_fail (source); g_return_if_fail (res); if (!gcry_check_version (GCRYPT_VERSION)) { g_set_error (error, g_quark_from_static_string ("whoopsie-quark"), 0, "libcrypt version mismatch."); return; } gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); md_len = gcry_md_get_algo_dlen(GCRY_MD_SHA512); if (md_len != HASHLEN / 2) { g_set_error (error, g_quark_from_static_string ("whoopsie-quark"), 0, "Received an incorrect size for the SHA512 message digest"); return; } if (gcry_md_open (&sha512, GCRY_MD_SHA512, 0) || sha512 == NULL) { g_set_error (error, g_quark_from_static_string ("whoopsie-quark"), 0, "Failed to create a SHA512 message digest for the product_uuid."); return; } gcry_md_write (sha512, source, strlen (source)); gcry_md_final (sha512); id = gcry_md_read (sha512, GCRY_MD_SHA512); if (id == NULL) { g_set_error (error, g_quark_from_static_string ("whoopsie-quark"), 0, "Failed to read the SHA512 message digest for the product_uuid."); gcry_md_close (sha512); return; } whoopsie_hex_to_char (res, (const char*)id, md_len); gcry_md_close (sha512); } void whoopsie_identifier_generate (char** res, GError** error) { char* identifier = NULL; g_return_if_fail (res); whoopsie_identifier_get_system_uuid (&identifier, error); if ((!error || !(*error)) && identifier) goto out; if (error && *error) { g_error_free (*error); *error = NULL; } whoopsie_identifier_get_mac_address (&identifier, error); if ((!error || !(*error)) && identifier) goto out; return; out: *res = g_malloc (HASHLEN + 1); whoopsie_identifier_sha512 (identifier, *res, error); g_free (identifier); } whoopsie-0.2.24.5/src/utils.h0000664000000000000000000000227412220367022012570 0ustar /* whoopsie * * Copyright © 2011-2013 Canonical Ltd. * Author: Evan Dandrea * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UTILS_H #define UTILS_H struct response_string { char* p; size_t length; }; char* change_file_extension (const char* path, const char* extension); gboolean already_handled_report (const char* crash_file); gboolean mark_handled (const char* crash_file); void init_response_string (struct response_string* resp); void grow_response_string (struct response_string* resp, char* str, size_t length); void destroy_response_string (struct response_string* resp); #endif /* UTILS_H */ whoopsie-0.2.24.5/src/whoopsie.c0000664000000000000000000007217712321555645013305 0ustar /* whoopsie * * Copyright © 2011-2013 Canonical Ltd. * Author: Evan Dandrea * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #define _XOPEN_SOURCE #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bson/bson.h" #include "whoopsie.h" #include "utils.h" #include "connectivity.h" #include "monitor.h" #include "identifier.h" #include "logging.h" #include "globals.h" /* The length of time to wait before processing outstanding crashes, in seconds */ #define PROCESS_OUTSTANDING_TIMEOUT 7200 /* If true, we have an active Internet connection. True by default in case we * can't bring up GNetworkMonitor */ static gboolean online_state = TRUE; /* The URL of the crash database. */ static char* crash_db_url = NULL; /* Username we will run under */ static const char* username = "whoopsie"; /* The database identifier. Either: * - The system UUID, taken from the DMI tables and SHA-512 hashed * - The MAC address of the first non-loopback device, SHA-512 hashed */ static char* whoopsie_identifier = NULL; /* The URL for sending the initial crash report */ static char* crash_db_submit_url = NULL; /* The file path and descriptor for our instance lock */ static const char* lock_path = "/var/lock/whoopsie/lock"; static int lock_fd = 0; /* The report directory */ static const char* report_dir = "/var/crash"; /* Options */ int foreground = 0; #ifndef TEST static int assume_online = 0; static GOptionEntry option_entries[] = { { "foreground", 'f', 0, G_OPTION_ARG_NONE, &foreground, "Run in the foreground", NULL }, { "assume-online", 'a', 0, G_OPTION_ARG_NONE, &assume_online, "Always assume there is a route to $CRASH_DB_URL.", NULL }, { NULL } }; #endif static const char* acceptable_fields[] = { "ProblemType", "Date", "Traceback", "Signal", "PythonArgs", "Package", "SourcePackage", "PackageArchitecture", "Dependencies", "MachineType", "StacktraceAddressSignature", "ApportVersion", "DuplicateSignature", /* add_os_info */ "DistroRelease", "Uname", "Architecture", "NonfreeKernelModules", "LiveMediaBuild", /* add_user_info */ "UserGroups", /* add_proc_info */ "ExecutablePath", "InterpreterPath", "ExecutableTimestamp", "ProcCwd", "ProcEnviron", "ProcCmdline", "ProcStatus", "ProcMaps", "ProcAttrCurrent", /* add_gdb_info */ "Registers", "Disassembly", /* We do not need these since we retrace with ddebs on errors */ /* "Stacktrace", */ /* "ThreadStacktrace", */ /* used to repair the StacktraceAddressSignature if it is corrupt */ "StacktraceTop", "AssertionMessage", "ProcAttrCurrent", "CoreDump", /* add_kernel_crash_info */ "VmCore", /* We use package-from-proposed tag to determine if a problem is occuring * in the release-proposed pocket. */ "Tags", /* We need the OopsText field to be able to generate a crash signature from * KernelOops problems */ "OopsText", /* We use the UpgradeStatus field to determine the time between upgrading * and the initial error report */ "UpgradeStatus", /* We use the InstallationDate and InstallationMedia fields to determine * the time between installing and the initial error report */ "InstallationDate", "InstallationMedia", /* Fields we do not care about: */ /* We would only want this to see how many bugs would otherwise go * unreported: */ /* "UnreportableReason", */ /* We'll have our own count in the database. */ /* "CrashCounter", */ /* "Title", */ NULL, }; static gboolean is_acceptable_field (const char* field) { const char** p; g_return_val_if_fail (field, FALSE); p = acceptable_fields; while (*p) { if (strcmp (*p, field) == 0) return TRUE; p++; } return FALSE; } gboolean append_key_value (gpointer key, gpointer value, gpointer bson_string) { /* Takes a key and its value from a #GHashTable and adds it to a BSON string * as key and its string value. Return %FALSE on error. */ bson* str = (bson*) bson_string; char* k = (char*) key; char* v = (char*) value; /* We don't send the core dump in the first upload, as the server might not * need it */ if (!strcmp ("CoreDump", k)) return TRUE; if (!strcmp ("VmCore", k)) return TRUE; return bson_append_string (str, k, v) != BSON_ERROR; } size_t server_response (char* ptr, size_t size, size_t nmemb, void* s) { struct response_string* resp = (struct response_string*) s; grow_response_string (resp, ptr, size * nmemb); return size * nmemb; } void split_string (char* head, char** tail) { g_return_if_fail (head); g_return_if_fail (tail); *tail = strchr (head, ' '); if (*tail) { **tail = '\0'; (*tail)++; } } gboolean bsonify (GHashTable* report, bson* b, const char** bson_message, int* bson_message_len) { /* Attempt to convert a #GHashTable of the report into a BSON string. * On error return %FALSE. */ GHashTableIter iter; gpointer key, value; *bson_message = NULL; *bson_message_len = 0; g_return_val_if_fail (report, FALSE); bson_init (b); if (!bson_data (b)) return FALSE; g_hash_table_iter_init (&iter, report); while (g_hash_table_iter_next (&iter, &key, &value)) { if (!append_key_value (key, value, b)) return FALSE; } if (bson_finish (b) == BSON_ERROR) return FALSE; *bson_message = bson_data (b); *bson_message_len = bson_size (b); if (*bson_message_len > 0 && *bson_message) return TRUE; else return FALSE; } int upload_report (const char* message_data, int message_len, struct response_string* s) { CURL* curl = NULL; CURLcode result_code = 0; long response_code = 0; struct curl_slist* list = NULL; g_return_val_if_fail (message_data, -1); /* TODO use curl_share for DNS caching. */ /* Repeated calls to curl_global_init will have no effect. */ if (curl_global_init (CURL_GLOBAL_SSL)) { log_msg ("Unable to initialize curl.\n\n"); exit (EXIT_FAILURE); } if ((curl = curl_easy_init ()) == NULL) { log_msg ("Couldn't init curl.\n"); return FALSE; } curl_easy_setopt (curl, CURLOPT_POST, 1); curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 1); list = curl_slist_append (list, "Content-Type: application/octet-stream"); list = curl_slist_append (list, "X-Whoopsie-Version: " VERSION); curl_easy_setopt (curl, CURLOPT_URL, crash_db_submit_url); curl_easy_setopt (curl, CURLOPT_HTTPHEADER, list); curl_easy_setopt (curl, CURLOPT_POSTFIELDSIZE, message_len); curl_easy_setopt (curl, CURLOPT_POSTFIELDS, (void*)message_data); curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, server_response); curl_easy_setopt (curl, CURLOPT_WRITEDATA, s); curl_easy_setopt (curl, CURLOPT_VERBOSE, 0L); result_code = curl_easy_perform (curl); curl_slist_free_all(list); curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &response_code); log_msg ("Sent; server replied with: %s\n", curl_easy_strerror (result_code)); log_msg ("Response code: %ld\n", response_code); curl_easy_cleanup (curl); if (result_code != CURLE_OK) return result_code; else return response_code; } void destroy_key_and_value (gpointer key, gpointer value, gpointer user_data) { if (key) g_free (key); /* The value may be "", which is allocated on the stack. */ if (value && *(char*)value != '\0') g_free (value); } GHashTable* parse_report (const char* report_path, gboolean full_report, GError** error) { /* We'll eventually modify the contents of the report, rather than sending * it as-is, to make it more amenable to what the server has to stick in * the database, and thus creating less work server-side. */ GMappedFile* fp = NULL; GHashTable* hash_table = NULL; gchar* contents = NULL; gsize file_len = 0; /* Our position in the file. */ gchar* p = NULL; /* The end or length of the token. */ gchar* token_p = NULL; char* key = NULL; char* value = NULL; gchar* value_p = NULL; GError* err = NULL; gchar* end = NULL; int value_length; int value_pos; gboolean ignore_field = FALSE; g_return_val_if_fail (report_path, NULL); if (g_file_test (report_path, G_FILE_TEST_IS_SYMLINK) || !g_file_test (report_path, G_FILE_TEST_IS_REGULAR)) { g_set_error (error, g_quark_from_static_string ("whoopsie-quark"), 0, "%s is a symlink or is not a regular file.", report_path); return NULL; } /* TODO handle the file being modified underneath us. */ fp = g_mapped_file_new (report_path, FALSE, &err); if (err) { g_set_error (error, g_quark_from_static_string ("whoopsie-quark"), 0, "Unable to map report: %s", err->message); g_error_free (err); goto error; } contents = g_mapped_file_get_contents (fp); file_len = g_mapped_file_get_length (fp); end = contents + file_len; hash_table = g_hash_table_new (g_str_hash, g_str_equal); p = contents; while (p < end) { /* We're either at the beginning of the file or the start of a line, * otherwise this report is corrupted. */ if (!(p == contents || *(p-1) == '\n')) { g_set_error (error, g_quark_from_static_string ("whoopsie-quark"), 0, "Malformed report."); goto error; } if (*p == ' ') { if (!key && !ignore_field) { g_set_error (error, g_quark_from_static_string ("whoopsie-quark"), 0, "Report may not start with a value."); goto error; } /* Skip the space. */ p++; token_p = p; while (token_p < end && *token_p != '\n') token_p++; if (!ignore_field) { /* The length of this value string */ value_length = token_p - p; if (value) { /* Space for the leading newline too. */ value_pos = value_p - value; value = g_realloc (value, value_pos + 1 + value_length + 1); value_p = value + value_pos; *value_p = '\n'; value_p++; } else { value = g_realloc (value, value_length + 1); value_p = value; } memcpy (value_p, p, value_length); value_p[value_length] = '\0'; for (char *c = value_p; c < value_p + value_length; c++) /* If c is a control character. */ if (*c >= '\0' && *c < ' ') *c = '?'; value_p += value_length; g_hash_table_insert (hash_table, key, value ? value : ""); } p = token_p + 1; } else { /* Reset the value pointer. */ value = NULL; /* Key. */ token_p = p; while (token_p < end) { if (*token_p != ':') { if (*token_p == '\n') { /* No colon character found on this line */ g_set_error (error, g_quark_from_static_string ("whoopsie-quark"), 0, "Report key must have a value."); goto error; } token_p++; } else if ((*(token_p + 1) == '\n' && *(token_p + 2) != ' ')) { /* The next line doesn't start with a value */ g_set_error (error, g_quark_from_static_string ("whoopsie-quark"), 0, "Report key must have a value."); goto error; } else { break; } } key = g_malloc ((token_p - p) + 1); memcpy (key, p, (token_p - p)); key[(token_p - p)] = '\0'; /* Replace any embedded NUL bytes. */ for (char *c = key; c < key + (token_p - p); c++) if (*c >= '\0' && *c < ' ') *c = '?'; /* Should we send this field? */ if (!full_report) ignore_field = !is_acceptable_field (key); /* Eat the semicolon. */ token_p++; /* Skip any leading spaces. */ while (token_p < end && *token_p == ' ') token_p++; /* Start of the value. */ p = token_p; while (token_p < end && *token_p != '\n') token_p++; if ((token_p - p) == 0) { /* Empty value. The key likely has a child. */ value = NULL; } else if (ignore_field) { value = NULL; } else { if (!strncmp ("base64", p, 6)) { /* Just a marker that the following lines are base64 * encoded. Don't include it in the value. */ value = NULL; } else { /* Value. */ value = g_malloc ((token_p - p) + 1); memcpy (value, p, (token_p - p)); value[(token_p - p)] = '\0'; for (char *c = value; c < value + (token_p - p); c++) if (*c >= '\0' && *c < ' ') *c = '?'; value_p = value + (token_p - p); } } p = token_p + 1; if (!ignore_field) g_hash_table_insert (hash_table, key, value ? value : ""); else g_free (key); } } g_mapped_file_unref (fp); return hash_table; error: if (hash_table) { g_hash_table_foreach (hash_table, destroy_key_and_value, NULL); g_hash_table_destroy (hash_table); } g_mapped_file_unref (fp); return NULL; } gboolean upload_core (const char* uuid, const char* arch, const char* core_data) { CURL* curl = NULL; CURLcode result_code = 0; long response_code = 0; struct curl_slist* list = NULL; char* crash_db_core_url = NULL; struct response_string s; g_return_val_if_fail (uuid, FALSE); g_return_val_if_fail (arch, FALSE); g_return_val_if_fail (core_data, FALSE); crash_db_core_url = g_strdup_printf ("%s/%s/submit-core/%s/%s", crash_db_url, uuid, arch, whoopsie_identifier); /* TODO use CURLOPT_READFUNCTION to transparently compress data with * Snappy. */ if ((curl = curl_easy_init ()) == NULL) { log_msg ("Couldn't init curl.\n"); g_free (crash_db_core_url); return FALSE; } init_response_string (&s); curl_easy_setopt (curl, CURLOPT_POST, 1); curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 1); list = curl_slist_append (list, "Content-Type: application/octet-stream"); list = curl_slist_append (list, "X-Whoopsie-Version: " VERSION); curl_easy_setopt (curl, CURLOPT_URL, crash_db_core_url); curl_easy_setopt (curl, CURLOPT_HTTPHEADER, list); curl_easy_setopt (curl, CURLOPT_POSTFIELDS, (void*)core_data); curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, server_response); curl_easy_setopt (curl, CURLOPT_WRITEDATA, &s); curl_easy_setopt (curl, CURLOPT_VERBOSE, 0L); result_code = curl_easy_perform (curl); curl_slist_free_all(list); curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &response_code); log_msg ("Sent; server replied with: %s\n", curl_easy_strerror (result_code)); log_msg ("Response code: %ld\n", response_code); curl_easy_cleanup (curl); g_free (crash_db_core_url); destroy_response_string (&s); return result_code == CURLE_OK && response_code == 200; } void handle_response (GHashTable* report, char* response_data) { char* command = NULL; char* core = NULL; char* arch = NULL; g_return_if_fail (report); /* Command could be CORE, which requests the core dump, BUG ######, if in a * development release, which points to the bug report, or UPDATE, if this * is fixed in an update. */ split_string (response_data, &command); if (command) { if (strcmp (command, "CORE") == 0) { core = g_hash_table_lookup (report, "CoreDump"); arch = g_hash_table_lookup (report, "Architecture"); if (core && arch) { if (!upload_core (response_data, arch, core)) /* We do not retry the upload. Once is a big enough hit to * their Internet connection, and we can always count on * the next person in line to send it. */ log_msg ("Upload of the core dump failed.\n"); } else log_msg ("Asked for a core dump that we don't have.\n"); } else log_msg ("Got command: %s\n", command); } } gboolean parse_and_upload_report (const char* crash_file) { GHashTable* report = NULL; gboolean success = FALSE; int message_len = 0; const char* message_data = NULL; struct response_string s; GError* error = NULL; bson b[1]; int response = 0; log_msg ("Parsing %s.\n", crash_file); report = parse_report (crash_file, FALSE, &error); if (!report) { if (error) { log_msg ("Unable to parse report (%s): %s\n", crash_file, error->message); g_error_free (error); } else { log_msg ("Unable to parse report (%s)\n", crash_file); } /* Do not keep trying to parse and upload this */ return TRUE; } if (!bsonify (report, b, &message_data, &message_len)) { log_msg ("Unable to bsonify report (%s)\n", crash_file); if (bson_data (b)) bson_destroy (b); /* Do not keep trying to parse and upload this */ success = TRUE; } else { log_msg ("Uploading %s.\n", crash_file); init_response_string (&s); response = upload_report (message_data, message_len, &s); if (bson_data (b)) bson_destroy (b); /* If the response code is 400, the server did not like what we sent it. * Sending the same thing again is not likely to change that */ /* TODO check that there aren't 400 responses that we care about seeing * again, such as a transient database failure. */ if (response == 200 || response == 400) success = TRUE; else success = FALSE; if (response > 200) { log_msg ("Server replied with:\n"); log_msg ("%s\n", s.p); } if (response == 200 && s.length > 0) handle_response (report, s.p); destroy_response_string (&s); } g_hash_table_foreach (report, destroy_key_and_value, NULL); g_hash_table_destroy (report); return success; } gboolean process_existing_files (const char* report_dir) { GDir* dir = NULL; const gchar* file = NULL; const gchar* ext = NULL; char* upload_file = NULL; char* crash_file = NULL; dir = g_dir_open (report_dir, 0, NULL); while ((file = g_dir_read_name (dir)) != NULL) { upload_file = g_build_filename (report_dir, file, NULL); if (!upload_file) continue; ext = strrchr (upload_file, '.'); if (ext && strcmp(++ext, "upload") != 0) { g_free (upload_file); continue; } crash_file = change_file_extension (upload_file, ".crash"); if (!crash_file) { g_free (upload_file); continue; } if (already_handled_report (crash_file)) { g_free (upload_file); g_free (crash_file); continue; } else if (online_state && parse_and_upload_report (crash_file)) { if (!mark_handled (crash_file)) log_msg ("Unable to mark report as seen (%s)\n", crash_file); } g_free (upload_file); g_free (crash_file); } g_dir_close (dir); return TRUE; } void daemonize (void) { pid_t pid, sid; int i; struct rlimit rl = {0}; if (getrlimit (RLIMIT_NOFILE, &rl) < 0) { log_msg ("Could not get resource limits.\n"); exit (EXIT_FAILURE); } umask (0); pid = fork(); if (pid < 0) exit (EXIT_FAILURE); if (pid > 0) exit (EXIT_SUCCESS); sid = setsid (); if (sid < 0) exit (EXIT_FAILURE); if ((chdir ("/")) < 0) exit (EXIT_FAILURE); for (i = 0; i < rl.rlim_max && i < 1024; i++) { if (i != lock_fd) close (i); } if ((open ("/dev/null", O_RDWR) != 0) || (dup (0) != 1) || (dup (0) != 2)) { log_msg ("Could not redirect file descriptors to /dev/null.\n"); exit (EXIT_FAILURE); } } void exit_if_already_running (void) { int rc = 0; if (g_getenv ("APPORT_REPORT_DIR")) { /* keep lock file in custom report directory */ lock_path = g_build_filename (report_dir, "whoopsie_lock", NULL); } else { /* use system directory */ if (mkdir ("/var/lock/whoopsie", 0755) < 0) { if (errno != EEXIST) { log_msg ("Could not create lock directory.\n"); } } } log_msg ("Using lock path: %s\n", lock_path); lock_fd = open (lock_path, O_CREAT | O_RDWR, 0600); rc = flock (lock_fd, LOCK_EX | LOCK_NB); if (rc) { if (EWOULDBLOCK == errno) { log_msg ("Another instance is already running.\n"); exit (1); } else { log_msg ("Could not create lock file: %s\n", strerror (errno)); } } } char* get_crash_db_url (void) { const char* url = NULL; url = g_getenv ("CRASH_DB_URL"); if (url == NULL) return NULL; if ((strncasecmp ("http://", url, 7) || url[7] == '\0') && (strncasecmp ("https://", url, 8) || url[8] == '\0')) return NULL; return g_strdup (url); } void drop_privileges (GError** error) { struct passwd *pw = NULL; if (getuid () != 0) { if (g_getenv ("CRASH_DB_IDENTIFIER") == NULL) { g_set_error (error, g_quark_from_static_string ("whoopsie-quark"), 0, "You must be root to run this program, or set $CRASH_DB_IDENTIFIER."); } return; } if (!(pw = getpwnam (username))) { g_set_error (error, g_quark_from_static_string ("whoopsie-quark"), 0, "Failed to find user: %s", username); return; } /* Drop privileges */ if (setgroups (1, &pw->pw_gid) < 0 || setresgid (pw->pw_gid, pw->pw_gid, pw->pw_gid) < 0 || setresuid (pw->pw_uid, pw->pw_uid, pw->pw_uid) < 0) { g_set_error (error, g_quark_from_static_string ("whoopsie-quark"), 0, "Failed to become user: %s", username); return; } if (prctl (PR_SET_DUMPABLE, 1)) g_set_error (error, g_quark_from_static_string ("whoopsie-quark"), 0, "Failed to ensure core dump production."); if ((setenv ("USER", username, 1) < 0) || (setenv ("USERNAME", username, 1) < 0)) { g_set_error (error, g_quark_from_static_string ("whoopsie-quark"), 0, "Failed to set user environment variables."); return; } } void network_changed (gboolean available) { log_msg (available ? "online\n" : "offline\n"); if (!available) { online_state = FALSE; return; } if (online_state && available) return; online_state = available; if (online_state) process_existing_files (report_dir); } gboolean check_online_then_upload (const char* crash_file) { if (!online_state) { log_msg ("Not online; processing later (%s).\n", crash_file); return FALSE; } if (!parse_and_upload_report (crash_file)) { log_msg ("Could not upload; processing later (%s).\n", crash_file); return FALSE; } return TRUE; } void create_crash_directory (void) { struct passwd *pw = NULL; if (mkdir (report_dir, 0755) < 0) { if (errno != EEXIST) { log_msg ("Could not create non-existent report_directory to monitor (%d): %s.\n", errno, report_dir); exit (EXIT_FAILURE); } } else { /* Only change the permissions if we've just created it */ if (!(pw = getpwnam (username))) { log_msg ("Could not find user, %s.\n", username); exit (EXIT_FAILURE); } if (chown (report_dir, -1, pw->pw_gid) < 0) { log_msg ("Could not change ownership of %s.\n", report_dir); exit (EXIT_FAILURE); } if (chmod (report_dir, 03777) < 0) { log_msg ("Could not change permissions on %s.\n", report_dir); exit (EXIT_FAILURE); } } } #ifndef TEST static GMainLoop* loop = NULL; static void parse_arguments (int* argc, char** argv[]) { GError* err = NULL; GOptionContext* context; context = g_option_context_new (NULL); g_option_context_add_main_entries (context, option_entries, NULL); if (!g_option_context_parse (context, argc, argv, &err)) { log_msg ("whoopsie: %s\n", err->message); g_error_free (err); exit (EXIT_FAILURE); } g_option_context_free (context); } static void handle_signals (int signo) { if (loop) g_main_loop_quit (loop); else exit (0); } static void setup_signals (void) { struct sigaction action; sigset_t mask; sigemptyset (&mask); action.sa_handler = handle_signals; action.sa_mask = mask; action.sa_flags = 0; sigaction (SIGTERM, &action, NULL); sigaction (SIGINT, &action, NULL); } int main (int argc, char** argv) { GError* err = NULL; const gchar* env; GFileMonitor* monitor; setup_signals (); parse_arguments (&argc, &argv); if (!foreground) { open_log (); log_msg ("whoopsie " VERSION " starting up.\n"); } if ((crash_db_url = get_crash_db_url ()) == NULL) { log_msg ("Could not get crash database location.\n"); exit (EXIT_FAILURE); } /* environment might change report directory and identifier */ env = g_getenv ("APPORT_REPORT_DIR"); if (env != NULL && *env != '\0') report_dir = g_strdup (env); env = g_getenv ("CRASH_DB_IDENTIFIER"); if (env != NULL) whoopsie_identifier = g_strdup (env); else whoopsie_identifier_generate (&whoopsie_identifier, &err); if (err) { log_msg ("%s\n", err->message); g_error_free (err); err = NULL; crash_db_submit_url = strdup (crash_db_url); } else { crash_db_submit_url = g_strdup_printf ("%s/%s", crash_db_url, whoopsie_identifier); } create_crash_directory (); drop_privileges (&err); if (err) { log_msg ("%s\n", err->message); g_error_free (err); exit (EXIT_FAILURE); } exit_if_already_running (); if (!foreground) { close_log (); daemonize (); open_log(); } #if GLIB_MAJOR_VERSION <= 2 && GLIB_MINOR_VERSION < 35 /* Deprecated in glib 2.35/2.36. */ g_type_init (); #endif monitor = monitor_directory (report_dir, check_online_then_upload); if (!monitor) exit (EXIT_FAILURE); if (!assume_online) monitor_connectivity (crash_db_url, network_changed); process_existing_files (report_dir); g_timeout_add_seconds (PROCESS_OUTSTANDING_TIMEOUT, (GSourceFunc) process_existing_files, (gpointer) report_dir); loop = g_main_loop_new (NULL, FALSE); g_main_loop_run (loop); unmonitor_directory (monitor, check_online_then_upload); if (!assume_online) unmonitor_connectivity (); close_log (); g_unlink (lock_path); close (lock_fd); curl_global_cleanup (); g_free (crash_db_url); g_free (crash_db_submit_url); return 0; } #endif whoopsie-0.2.24.5/src/logging.h0000664000000000000000000000153312267517325013071 0ustar /* whoopsie * * Copyright © 2011-2013 Canonical Ltd. * Author: Evan Dandrea * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef LOGGING_H #define LOGGING_H void log_msg (const char* fmt, ...); void open_log (void); void close_log (void); #endif /* LOGGING_H */ whoopsie-0.2.24.5/src/monitor.h0000664000000000000000000000203512220367022013112 0ustar /* whoopsie * * Copyright © 2011-2013 Canonical Ltd. * Author: Evan Dandrea * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MONITOR_H #define MONITOR_H #include #include typedef gboolean (*FileCreationCallback) (const char* crash_file); GFileMonitor* monitor_directory (const char* directory, FileCreationCallback callback); void unmonitor_directory (GFileMonitor* monitor, FileCreationCallback callback); #endif /* MONITOR_H */ whoopsie-0.2.24.5/src/monitor.c0000664000000000000000000000657312220367022013120 0ustar /* whoopsie * * Copyright © 2011-2013 Canonical Ltd. * Author: Evan Dandrea * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #define _XOPEN_SOURCE #define _GNU_SOURCE #include #include #include #include #include #include "monitor.h" #include "utils.h" #include "logging.h" void create_file (const char* upload, FileCreationCallback callback) { char* upload_file = NULL; char* crash_file = NULL; g_return_if_fail (upload); crash_file = change_file_extension (upload, ".crash"); if (!crash_file) { log_msg ("Unable to parse the upload file path.\n"); return; } if (already_handled_report (crash_file)) { /* Ensure that if we ended up here because of an error, that we do not * attempt to process this report again. */ if (!mark_handled (crash_file)) log_msg ("Unable to mark report as seen (%s).\n", crash_file); } else if (callback (crash_file)) { /* We successfully uploaded the report */ if (!mark_handled (crash_file)) log_msg ("Unable to mark report as seen (%s).\n", crash_file); } g_free (crash_file); g_free (upload_file); } void changed_event (GFileMonitor* monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type, FileCreationCallback callback) { char* path = NULL; const char *ext = NULL; path = g_file_get_path (file); if (!path) return; /* Find the file extension. */ ext = strrchr (path, '.'); if (!ext || !path) { if (path) g_free (path); return; } if (strcmp (ext, ".upload") == 0) { if ((event_type == G_FILE_MONITOR_EVENT_CREATED) || (event_type == G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED)) create_file (path, callback); } g_free (path); } GFileMonitor* monitor_directory (const char* directory, FileCreationCallback callback) { GFileMonitor* monitor = NULL; GError* err = NULL; GFile* path = NULL; g_return_val_if_fail (directory, NULL); if (access (directory, R_OK) < 0) { log_msg ("Could not read directory (%d): %s\n", errno, directory); } path = g_file_new_for_path (directory); monitor = g_file_monitor_directory (path, G_FILE_MONITOR_NONE, NULL, &err); g_object_unref (path); if (err) { log_msg ("Unable to monitor %s: %s\n", directory, err->message); g_error_free (err); return NULL; } g_signal_connect (monitor, "changed", G_CALLBACK (changed_event), callback); return monitor; } void unmonitor_directory (GFileMonitor* monitor, FileCreationCallback callback) { g_assert (1 == g_signal_handlers_disconnect_by_func (monitor, changed_event, callback)); g_object_unref (monitor); } whoopsie-0.2.24.5/src/globals.h0000664000000000000000000000145612220367022013054 0ustar /* whoopsie * * Copyright © 2011-2013 Canonical Ltd. * Author: Evan Dandrea * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef GLOBALS_H #define GLOBALS_H /* Options */ extern int foreground; #endif /* GLOBALS_H */ whoopsie-0.2.24.5/src/utils.c0000664000000000000000000000774212251715354012601 0ustar /* whoopsie * * Copyright © 2011-2013 Canonical Ltd. * Author: Evan Dandrea * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include "utils.h" void init_response_string (struct response_string* resp) { g_return_if_fail (resp); resp->length = 0; resp->p = g_malloc (1); resp->p[0] = '\0'; } void grow_response_string (struct response_string* resp, char* str, size_t length) { size_t new_length; g_return_if_fail (resp); g_return_if_fail (str); new_length = resp->length + length; resp->p = g_realloc (resp->p, new_length + 1); memcpy (resp->p + resp->length, str, length); resp->p[new_length] = '\0'; resp->length = new_length; } void destroy_response_string (struct response_string* resp) { if (!resp || !(resp->p)) return; g_free (resp->p); resp->p = NULL; resp->length = 0; } char* change_file_extension (const char* path, const char* extension) { /* Ex. (/var/crash/foo.crash, .upload) -> /var/crash/foo.upload */ char* new_path = NULL; const char* p = NULL; int len = 0; int i = 0; int ext_len = 0; if (!extension || !path) return NULL; /* Find the file extension. */ p = path; while (*p != '\0') { if (*p == '.') len = i; p++; i++; } if (len <= 0) return NULL; ext_len = strlen (extension); new_path = g_malloc (len + ext_len + 1); memcpy (new_path, path, len); strncpy (new_path + len, extension, ext_len); /* Ensure the new string is NUL terminated */ new_path[len + ext_len] = '\0'; return new_path; } gboolean already_handled_report (const char* crash_file) { /* If the .uploaded path exists and .uploaded is newer than .upload */ char* upload_file = NULL; char* uploaded_file = NULL; struct stat uploaded_stat; struct stat upload_stat; struct stat crash_stat; g_return_val_if_fail (crash_file, FALSE); if (stat (crash_file, &crash_stat) < 0) /* .crash doesn't exist; don't process. */ return TRUE; upload_file = change_file_extension (crash_file, ".upload"); if (!upload_file) /* Bogus crash file; don't process. */ return TRUE; if (stat (upload_file, &upload_stat) < 0) { /* .upload doesn't exist; don't process. */ g_free (upload_file); return TRUE; } g_free (upload_file); uploaded_file = change_file_extension (crash_file, ".uploaded"); if (!uploaded_file) /* Bogus crash file; don't process. */ return TRUE; if (stat (uploaded_file, &uploaded_stat) < 0) { /* .uploaded doesn't exist. */ g_free (uploaded_file); return FALSE; } g_free (uploaded_file); /* If .uploaded is newer than .upload, then we've already handled this * report */ return (uploaded_stat.st_mtime > upload_stat.st_mtime); } gboolean mark_handled (const char* crash_file) { char* uploaded_file = NULL; int fd; g_return_val_if_fail (crash_file, FALSE); uploaded_file = change_file_extension (crash_file, ".uploaded"); if (!uploaded_file) return FALSE; fd = creat (uploaded_file, 0600); g_free (uploaded_file); if (fd < 0) return FALSE; else { close (fd); return TRUE; } } whoopsie-0.2.24.5/src/tests/0000775000000000000000000000000012321555753012430 5ustar whoopsie-0.2.24.5/src/tests/test_monitor.c0000664000000000000000000001375312220367022015317 0ustar /* whoopsie * * Copyright © 2011-2013 Canonical Ltd. * Author: Evan Dandrea * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "../src/monitor.h" static GMainLoop* loop = NULL; int foreground = 1; gboolean _test_callback_never_triggered_callback (const char* path) { g_main_loop_quit (loop); g_test_fail (); return FALSE; } void test_callback_never_triggered (void) { GFileMonitor* monitor = NULL; monitor = monitor_directory ("/fake", _test_callback_never_triggered_callback); loop = g_main_loop_new (NULL, FALSE); g_timeout_add_seconds (0.5, (GSourceFunc) g_main_loop_quit, loop); g_main_loop_run (loop); if (monitor) unmonitor_directory (monitor, _test_callback_never_triggered_callback); g_main_loop_unref (loop); } gboolean _create_crash_file (const char* directory) { char* path = NULL; int fd; path = g_strdup_printf ("%s/fake.crash", directory); fd = creat (path, 0600); if (fd < 0) { g_warning ("Couldn't create temporary crash file."); g_test_fail (); g_free (path); return FALSE; } close (fd); g_free (path); return FALSE; } gboolean _create_upload_file (const char* directory) { char* path = NULL; int fd; struct stat path_stat; struct utimbuf new_time; path = g_strdup_printf ("%s/fake.upload", directory); fd = creat (path, 0600); if (fd < 0) { g_warning ("Couldn't create temporary upload file."); g_test_fail (); g_free (path); return FALSE; } close (fd); stat (path, &path_stat); new_time.actime = path_stat.st_atime; new_time.modtime = path_stat.st_mtime + 1; if (utime (path, &new_time) < 0) { g_warning ("Couldn't update utime."); g_test_fail (); } g_free (path); return FALSE; } gboolean _test_callback_triggered_once (const char* crash_file) { if (!g_str_has_suffix (crash_file, ".crash")) g_test_fail (); g_main_loop_quit (loop); return TRUE; } void _test_callback_timeout (void) { g_main_loop_quit (loop); g_test_fail (); } void test_callback_triggered_once (void) { char template[12] = "/tmp/XXXXXX"; char* path = NULL; const char* fake_files[] = { "crash", "upload", "uploaded", NULL }; const char** p = fake_files; struct stat fake_stat; int id; GFileMonitor* monitor = NULL; mktemp (template); if (*template == '\0') { g_warning ("Couldn't create temporary file."); g_test_fail (); return; } if (mkdir (template, 0755) < 0) { g_warning ("Couldn't create temporary directory."); g_test_fail (); return; } g_idle_add ((GSourceFunc) _create_crash_file, template); g_idle_add ((GSourceFunc) _create_upload_file, template); id = g_timeout_add_seconds (5, (GSourceFunc) _test_callback_timeout, NULL); monitor = monitor_directory (template, _test_callback_triggered_once); loop = g_main_loop_new (NULL, FALSE); g_main_loop_run (loop); if (monitor) unmonitor_directory (monitor, _test_callback_triggered_once); g_source_remove (id); g_main_loop_unref (loop); while (*p) { path = g_strdup_printf ("%s/fake.%s", template, *p); if (stat (path, &fake_stat)) { g_warning ("Expected file %s doesn't exist.", path); g_test_fail (); g_free (path); return; } if (unlink (path) < 0) { g_warning ("Couldn't remove temporary file, %s", path); g_test_fail (); g_free (path); return; } p++; g_free (path); } if (rmdir (template) < 0) { g_warning ("Couldn't remove temporary directory."); g_test_fail (); return; } } void test_callback_not_triggered_without_upload_file (void) { char template[12] = "/tmp/XXXXXX"; int id; GFileMonitor* monitor = NULL; mktemp (template); if (*template == '\0') { g_warning ("Couldn't create temporary file."); g_test_fail (); return; } if (mkdir (template, 0755) < 0) { g_warning ("Couldn't create temporary directory."); g_test_fail (); return; } g_idle_add ((GSourceFunc) _create_crash_file, template); loop = g_main_loop_new (NULL, FALSE); id = g_timeout_add_seconds (2, (GSourceFunc) g_main_loop_quit, loop); monitor = monitor_directory (template, _test_callback_never_triggered_callback); g_main_loop_run (loop); if (monitor) unmonitor_directory (monitor, _test_callback_never_triggered_callback); g_source_remove (id); g_main_loop_unref (loop); } int main (int argc, char** argv) { #if GLIB_MAJOR_VERSION <= 2 && GLIB_MINOR_VERSION < 35 /* Deprecated in glib 2.35/2.36. */ g_type_init (); #endif g_test_init (&argc, &argv, NULL); g_test_add_func ("/whoopsie/callback-never-triggered", test_callback_never_triggered); g_test_add_func ("/whoopsie/callback-triggered-once", test_callback_triggered_once); g_test_add_func ("/whoopsie/callback-not-triggered-without-upload-file", test_callback_not_triggered_without_upload_file); return g_test_run (); } whoopsie-0.2.24.5/src/tests/test_utils.c0000664000000000000000000001412112265561174014773 0ustar /* whoopsie * * Copyright © 2011-2013 Canonical Ltd. * Author: Evan Dandrea * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #define _XOPEN_SOURCE #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include "../src/utils.h" static void test_response_string (void) { struct response_string s; init_response_string (&s); grow_response_string (&s, "foo", 3); grow_response_string (&s, "bar", 3); if (s.p[6] != '\0' || strcmp (s.p, "foobar") != 0) g_test_fail (); if (s.length != 6) g_test_fail (); destroy_response_string (&s); if (s.p != NULL || s.length != 0) g_test_fail (); } static void test_change_file_extension (void) { char* new = NULL; new = change_file_extension (NULL, ".upload"); if (new) { g_test_fail (); } g_free (new); new = change_file_extension ("", ".upload"); if (new) { g_test_fail (); } g_free (new); new = change_file_extension ("foo", ".upload"); if (new) { g_test_fail (); } g_free (new); new = change_file_extension ("foo.crash", ""); if (!new) { g_test_fail (); } else if (strcmp (new, "foo") != 0) { g_test_fail (); } g_free (new); new = change_file_extension ("foo.crash", ".upload"); if (!new) g_test_fail (); else if (strcmp (new, "foo.upload") != 0) { g_test_fail (); } g_free (new); } static void test_already_handled_report (void) { int fd; char template[12] = "/tmp/XXXXXX"; char* crash_file = NULL; char* uploaded_file = NULL; char* upload_file = NULL; struct stat upload_stat; struct stat uploaded_stat; struct utimbuf new_time; mktemp (template); if (*template == '\0') g_warning ("Couldn't create temporary file."); crash_file = g_strdup_printf ("%s.crash", template); upload_file = g_strdup_printf ("%s.upload", template); uploaded_file = g_strdup_printf ("%s.uploaded", template); /* .crash file does not exist */ if (!already_handled_report (crash_file)) g_test_fail (); /* Crash file without an extension */ fd = creat (template, 0600); close (fd); if (!already_handled_report (template)) g_test_fail (); if (unlink (template) < 0) g_warning ("Unable to remove %s.", template); /* .uploaded file does not exist (we haven't processed this crash) */ fd = creat (crash_file, 0600); close (fd); fd = creat (upload_file, 0600); close (fd); if (already_handled_report (crash_file)) g_test_fail (); if (unlink (crash_file) < 0) g_warning ("Unable to remove %s.", crash_file); if (unlink (upload_file) < 0) g_warning ("Unable to remove %s.", upload_file); /* .uploaded file is newer than .crash file (we've processed this crash) */ fd = creat (crash_file, 0600); close (fd); fd = creat (upload_file, 0600); close (fd); fd = creat (uploaded_file, 0600); close (fd); stat (uploaded_file, &uploaded_stat); new_time.actime = uploaded_stat.st_atime; new_time.modtime = uploaded_stat.st_mtime + 1; if (utime (uploaded_file, &new_time) < 0) g_warning ("Could not update utime."); if (!already_handled_report (crash_file)) g_test_fail (); /* .upload file is newer than .uploaded file (we haven't processed this * updated crash) */ stat (upload_file, &upload_stat); new_time.actime = upload_stat.st_atime; new_time.modtime = uploaded_stat.st_mtime + 2; if (utime (upload_file, &new_time) < 0) g_warning ("Could not update utime."); if (already_handled_report (crash_file)) g_test_fail (); if (unlink (crash_file) < 0) g_warning ("Unable to remove %s.", crash_file); if (unlink (upload_file) < 0) g_warning ("Unable to remove %s.", upload_file); if (unlink (uploaded_file) < 0) g_warning ("Unable to remove %s.", uploaded_file); g_free (crash_file); g_free (upload_file); g_free (uploaded_file); } static void test_mark_handled (void) { char template[12] = "/tmp/XXXXXX"; char* crash_file = NULL; char* uploaded_file = NULL; struct stat uploaded_stat; mktemp (template); crash_file = g_strdup_printf ("%s.crash", template); uploaded_file = g_strdup_printf ("%s.uploaded", template); if (!mark_handled (crash_file)) { g_test_fail (); goto out; } if (stat (uploaded_file, &uploaded_stat) < 0) { g_test_fail (); goto out; } if (!(S_ISREG(uploaded_stat.st_mode))) g_test_fail (); if ((uploaded_stat.st_mode & 0600) != 0600) g_test_fail (); if (unlink (uploaded_file) < 0) g_warning ("Unable to remove %s.", uploaded_file); out: if (crash_file) g_free (crash_file); if (uploaded_file) g_free (uploaded_file); } int main (int argc, char** argv) { #if GLIB_MAJOR_VERSION <= 2 && GLIB_MINOR_VERSION < 35 /* Deprecated in glib 2.35/2.36. */ g_type_init (); #endif g_test_init (&argc, &argv, NULL); g_test_add_func ("/whoopsie/change-file-extension", test_change_file_extension); g_test_add_func ("/whoopsie/already-handled-report", test_already_handled_report); g_test_add_func ("/whoopsie/response-string", test_response_string); g_test_add_func ("/whoopsie/mark-handled", test_mark_handled); return g_test_run (); } whoopsie-0.2.24.5/src/tests/test_logging.c0000664000000000000000000001060312267517325015263 0ustar /* whoopsie * * Copyright © 2011-2013 Canonical Ltd. * Author: Evan Dandrea * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #define _XOPEN_SOURCE 500 #include #include #include #include #include #include #include #include #include #include "../src/globals.h" #include "../src/logging.h" int foreground; void test_logging (void) { char template[] = "/tmp/XXXXXX"; char buf[512] = {0}; FILE* fp = NULL; const char *expected = "Hello.\n"; foreground = 1; int fd; fpos_t pos; /* http://c-faq.com/stdio/undofreopen.html */ fflush (stdout); fgetpos (stdout, &pos); fd = dup (fileno (stdout)); tmpnam (template); if (freopen (template, "w", stdout) == NULL) { g_printerr ("Could not open temporary stdout (%d).\n", errno); g_test_fail (); goto out; } /* Test opening, writing to, and closing the log. */ open_log (); log_msg ("Hello.\n"); close_log (); /* Did we write a log file? */ if (access (template, F_OK) < 0) { g_printerr ("We did not write anything to stdout (%d).\n", errno); g_test_fail (); } /* Now ensure we've logged exactly what was expected. */ if ((fp = fopen (template, "r")) == NULL) { g_printerr ("Could not open temporary stdout (%d).\n", errno); g_test_fail (); } fread (buf, 512, 1, fp); if (strcmp (buf, expected) != 0) { g_printerr ("We did not write what was expected to stdout:\n%s", buf); g_test_fail (); } /* Clean up */ if (fclose (fp)) g_printerr ("Could not close the file pointer (%d).\n", errno); if (unlink (template) < 0) { if (errno != ENOENT) g_test_fail (); } out: dup2 (fd, fileno (stdout)); close (fd); clearerr (stdout); fsetpos (stdout, &pos); } void test_logging_syslog (void) { char template[] = "/tmp/XXXXXX"; char buf[512] = {0}; FILE* fp = NULL; extern int logging_flags; const char *expected = "whoopsie: Hello.\n"; logging_flags = LOG_PERROR; foreground = 0; int fd; fpos_t pos; /* http://c-faq.com/stdio/undofreopen.html */ fflush (stderr); fgetpos (stderr, &pos); fd = dup (fileno (stderr)); tmpnam (template); if (freopen (template, "w", stderr) == NULL) { g_print ("Could not open temporary stderr (%d).\n", errno); g_test_fail (); goto out; } open_log (); log_msg ("Hello.\n"); fflush (stderr); close_log (); /* Did we write a log file? */ if (access (template, F_OK) < 0) { g_print ("We did not write anything to stderr (%d).\n", errno); g_test_fail (); } /* Now ensure we've logged exactly what was expected. */ if ((fp = fopen (template, "r")) == NULL) { g_print ("Could not open temporary stderr (%d).\n", errno); g_test_fail (); } fread (buf, 512, 1, fp); if (strcmp (buf, expected) != 0) { g_print ("We did not write what was expected to stderr:\n%s", buf); g_test_fail (); } /* Clean up */ if (fclose (fp)) g_print ("Could not close the file pointer (%d).\n", errno); if (unlink (template) < 0) { if (errno != ENOENT) g_test_fail (); } out: dup2 (fd, fileno (stderr)); close (fd); clearerr (stderr); fsetpos (stderr, &pos); } int main (int argc, char** argv) { #if GLIB_MAJOR_VERSION <= 2 && GLIB_MINOR_VERSION < 35 /* Deprecated in glib 2.35/2.36. */ g_type_init (); #endif g_test_init (&argc, &argv, NULL); g_test_add_func ("/whoopsie/logging", test_logging); g_test_add_func ("/whoopsie/logging-syslog", test_logging_syslog); return g_test_run (); } whoopsie-0.2.24.5/src/tests/data/0000775000000000000000000000000012321555753013341 5ustar whoopsie-0.2.24.5/src/tests/data/_usr_bin_gedit.1000.crash0000664000000000000000000004504112061665776017732 0ustar ProblemType: Crash Architecture: amd64 Package: gedit Date: Tue Nov 8 17:48:59 2011 DistroRelease: Ubuntu 11.10 ExecutablePath: /usr/bin/gedit ProcCmdline: gedit ProcCwd: /home/evan ProcEnviron: LANG=en_GB.UTF-8 LANGUAGE=en_GB:en LC_CTYPE=en_GB.UTF-8 PATH=(custom, no user) SHELL=/bin/zsh ProcMaps: 00400000-00401000 r-xp 00000000 08:01 1050137 /usr/bin/gedit 00600000-00601000 r--p 00000000 08:01 1050137 /usr/bin/gedit 00601000-00602000 rw-p 00001000 08:01 1050137 /usr/bin/gedit 7f9c66feb000-7f9c67180000 r-xp 00000000 08:01 921784 /lib/x86_64-linux-gnu/libc-2.13.so 7f9c67180000-7f9c6737f000 ---p 00195000 08:01 921784 /lib/x86_64-linux-gnu/libc-2.13.so 7f9c6737f000-7f9c67383000 r--p 00194000 08:01 921784 /lib/x86_64-linux-gnu/libc-2.13.so 7f9c67383000-7f9c67384000 rw-p 00198000 08:01 921784 /lib/x86_64-linux-gnu/libc-2.13.so 7f9c67384000-7f9c6738a000 rw-p 00000000 00:00 0 7f9c6738a000-7f9c673ab000 r-xp 00000000 08:01 921915 /lib/x86_64-linux-gnu/ld-2.13.so 7f9c67593000-7f9c67596000 rw-p 00000000 00:00 0 7f9c675a8000-7f9c675aa000 rw-p 00000000 00:00 0 7f9c675aa000-7f9c675ab000 r--p 00020000 08:01 921915 /lib/x86_64-linux-gnu/ld-2.13.so 7f9c675ab000-7f9c675ad000 rw-p 00021000 08:01 921915 /lib/x86_64-linux-gnu/ld-2.13.so 7fff5bfcf000-7fff5bff0000 rw-p 00000000 00:00 0 [stack] 7fff5bfff000-7fff5c000000 r-xp 00000000 00:00 0 [vdso] ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall] ProcStatus: Name: gedit State: S (sleeping) Tgid: 5378 Pid: 5378 PPid: 5314 TracerPid: 0 Uid: 1000 1000 1000 1000 Gid: 1000 1000 1000 1000 FDSize: 64 Groups: 4 20 24 46 116 118 124 1000 VmPeak: 4096 kB VmSize: 4024 kB VmLck: 0 kB VmHWM: 324 kB VmRSS: 324 kB VmData: 48 kB VmStk: 136 kB VmExe: 4 kB VmLib: 1752 kB VmPTE: 28 kB VmSwap: 0 kB Threads: 1 SigQ: 0/5832 SigPnd: 0000000000000000 ShdPnd: 0000000000000000 SigBlk: 0000000000000000 SigIgn: 0000000000000000 SigCgt: 0000000000000000 CapInh: 0000000000000000 CapPrm: 0000000000000000 CapEff: 0000000000000000 CapBnd: ffffffffffffffff Cpus_allowed: 1 Cpus_allowed_list: 0 Mems_allowed: 00000000,00000001 Mems_allowed_list: 0 voluntary_ctxt_switches: 2 nonvoluntary_ctxt_switches: 2 Signal: 11 Uname: Linux 3.0.0-12-generic x86_64 UserGroups: adm admin cdrom dialout lpadmin plugdev sambashare CoreDump: base64 H4sICAAAAAAC/0NvcmVEdW1wAA== 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 whoopsie-0.2.24.5/src/tests/data/crash/0000775000000000000000000000000012321555753014441 5ustar whoopsie-0.2.24.5/src/tests/data/crash/invalid_no_spaces0000664000000000000000000000037712061665776020063 0ustar ProblemType:Crash Architecture:amd64 Date:Thu Feb 2 17:09:31 2012 ProcEnviron: LANGUAGE=en_US:en LC_CTYPE=en_US.UTF-8 LC_COLLATE=en_US.UTF-8 PATH=(custom, user) LANG=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 SHELL=/bin/bash Uname:Linux 3.2.0-12-generic x86_64 whoopsie-0.2.24.5/src/tests/data/crash/valid0000664000000000000000000000041212061665776015470 0ustar ProblemType: Crash Architecture: amd64 Date: Thu Feb 2 17:09:31 2012 ProcEnviron: LANGUAGE=en_US:en LC_CTYPE=en_US.UTF-8 LC_COLLATE=en_US.UTF-8 PATH=(custom, user) LANG=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 SHELL=/bin/bash Uname: Linux 3.2.0-12-generic x86_64 whoopsie-0.2.24.5/src/tests/data/crash/invalid_key_leading_spaces0000664000000000000000000000041412061665776021712 0ustar ProblemType: Crash Architecture: amd64 Date: Thu Feb 2 17:09:31 2012 ProcEnviron: LANGUAGE=en_US:en LC_CTYPE=en_US.UTF-8 LC_COLLATE=en_US.UTF-8 PATH=(custom, user) LANG=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 SHELL=/bin/bash Uname: Linux 3.2.0-12-generic x86_64 whoopsie-0.2.24.5/src/tests/data/crash/invalid-file-is-dir/0000775000000000000000000000000012321555753020171 5ustar whoopsie-0.2.24.5/src/tests/data/crash/invalid_value0000664000000000000000000000041312061665776017214 0ustar ProblemType: Crash Architecture: amd64 Date: Thu Feb 2 17:09:31 2012 ProcEnviron: LANGUAGE=en_US:en LC_CTYPE=en_US.UTF-8 LC_COLLATE=en_US.UTF-8 PATH=(custom, user) LANG=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 SHELL=/bin/bash Uname: Linux 3.2.0-12-generic x86_64 whoopsie-0.2.24.5/src/tests/data/crash/invalid_key_embedded_nul0000664000000000000000000000041312061665776021357 0ustar ProblemType: Crash Architecture: amd64 Date: Thu Feb 2 17:09:31 2012 ProcEnviron: LANGUAGE=en_US:en LC_CTYPE=en_US.UTF-8 LC_COLLATE=en_US.UTF-8 PATH=(custom, user) LANG=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 SHELL=/bin/bash Uname: Linux 3.2.0-12-generic x86_64 whoopsie-0.2.24.5/src/tests/data/crash/invalid_trailing20000664000000000000000000000034412061665776017776 0ustar ProblemType: Crash Architecture: amd64 Date: Thu Feb 2 17:09:31 2012 ProcEnviron: LANGUAGE=en_US:en LC_CTYPE=en_US.UTF-8 LC_COLLATE=en_US.UTF-8 PATH=(custom, user) LANG=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 SHELL=/bin/bashwhoopsie-0.2.24.5/src/tests/data/crash/valid_empty_value0000664000000000000000000000006312061665776020104 0ustar ProblemType: Crash ProcEnviron: ProcMaps: 004... whoopsie-0.2.24.5/src/tests/data/crash/invalid_value_embedded_nul0000664000000000000000000000041312061665776021703 0ustar ProblemType: Crash Architecture: amd64 Date: Thu Feb 2 17:09:31 2012 ProcEnviron: LANGUAGE=en_US:en LC_CTYPE=en_US.UTF-8 LC_COLLATE=en_US.UTF-8 PATH=(custom, user) LANG=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 SHELL=/bin/bash Uname: Linux 3.2.0-12-generic x86_64 whoopsie-0.2.24.5/src/tests/data/crash/invalid_key_leading_spaces20000664000000000000000000000041312061665776021773 0ustar ProblemType: Crash Architecture: amd64 Date: Thu Feb 2 17:09:31 2012 ProcEnviron: LANGUAGE=en_US:en LC_CTYPE=en_US.UTF-8 LC_COLLATE=en_US.UTF-8 PATH=(custom, user) LANG=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 SHELL=/bin/bash Uname: Linux 3.2.0-12-generic x86_64 whoopsie-0.2.24.5/src/tests/data/crash/invalid_value20000664000000000000000000000041312061665776017276 0ustar ProblemType: Crash Architecture: amd64 Date: Thu Feb 2 17:09:31 2012 ProcEnviron: LANGUAGE=en_US:en LC_CTYPE =en_US.UTF-8 LC_COLLATE=en_US.UTF-8 PATH=(custom, user) LANG=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 SHELL=/bin/bash Uname: Linux 3.2.0-12-generic x86_64 whoopsie-0.2.24.5/src/tests/data/crash/invalid_key_special_chars0000664000000000000000000000041312061665776021550 0ustar ProblemType: Cr ash Architecture: amd64 Date: Thu Feb 2 17:09:31 2012 ProcEnviron: LANGUAGE=en_US:en LC_CTYPE=en_US.UTF-8 LC_COLLATE=en_US.UTF-8 PATH=(custom, user) LANG=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 SHELL=/bin/bash Uname: Linux 3.2.0-12-generic x86_64 whoopsie-0.2.24.5/src/tests/data/crash/invalid_symlink0000777000000000000000000000000012321555753021765 2../etc/passwdustar whoopsie-0.2.24.5/src/tests/data/crash/invalid_key_no_value0000664000000000000000000000040312061665776020557 0ustar ProblemType Architecture: amd64 Date: Thu Feb 2 17:09:31 2012 ProcEnviron: LANGUAGE=en_US:en LC_CTYPE=en_US.UTF-8 LC_COLLATE=en_US.UTF-8 PATH=(custom, user) LANG=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 SHELL=/bin/bash Uname: Linux 3.2.0-12-generic x86_64 whoopsie-0.2.24.5/src/tests/data/crash/invalid_key_no_value20000664000000000000000000000040412061665776020642 0ustar ProblemType: Architecture: amd64 Date: Thu Feb 2 17:09:31 2012 ProcEnviron: LANGUAGE=en_US:en LC_CTYPE=en_US.UTF-8 LC_COLLATE=en_US.UTF-8 PATH=(custom, user) LANG=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 SHELL=/bin/bash Uname: Linux 3.2.0-12-generic x86_64 whoopsie-0.2.24.5/src/tests/data/crash/unicode0000664000000000000000000000245412061665776016027 0ustar ProblemType: Crash Architecture: amd64 Date: Wed Feb 22 13:10:30 2012 DistroRelease: Ubuntu 12.04 ExecutablePath: /usr/bin/gedit ExecutableTimestamp: 1328568261 ProcCmdline: gedit ♥ ProcCwd: /home/evan/bzr/apport.whoopsie ProcEnviron: LANG=es_ES.UTF-8 LANGUAGE=es_ES.UTF-8 LC_CTYPE=en_GB.UTF-8 PATH=(custom, no user) SHELL=/bin/zsh TERM=screen ProcStatus: Name: gedit State: S (sleeping) Tgid: 22560 Pid: 22560 PPid: 20817 TracerPid: 0 Uid: 1000 1000 1000 1000 Gid: 1000 1000 1000 1000 FDSize: 64 Groups: 4 20 24 46 116 118 124 1000 VmPeak: 486680 kB VmSize: 486480 kB VmLck: 0 kB VmPin: 0 kB VmHWM: 27672 kB VmRSS: 27672 kB VmData: 231960 kB VmStk: 136 kB VmExe: 640 kB VmLib: 23396 kB VmPTE: 544 kB VmSwap: 0 kB Threads: 4 SigQ: 0/5763 SigPnd: 0000000000000000 ShdPnd: 0000000000000000 SigBlk: 0000000000000000 SigIgn: 0000000000001000 SigCgt: 0000000180000000 CapInh: 0000000000000000 CapPrm: 0000000000000000 CapEff: 0000000000000000 CapBnd: ffffffffffffffff Cpus_allowed: 1 Cpus_allowed_list: 0 Mems_allowed: 00000000,00000001 Mems_allowed_list: 0 voluntary_ctxt_switches: 131 nonvoluntary_ctxt_switches: 352 Signal: 11 Uname: Linux 3.2.0-12-generic x86_64 UserGroups: adm admin cdrom dialout lpadmin plugdev sambashare whoopsie-0.2.24.5/src/tests/data/etc/0000775000000000000000000000000012321555753014114 5ustar whoopsie-0.2.24.5/src/tests/data/etc/passwd0000664000000000000000000000246712061665776015361 0ustar root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/bin/sh bin:x:2:2:bin:/bin:/bin/sh sys:x:3:3:sys:/dev:/bin/sh sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/usr/games:/bin/sh man:x:6:12:man:/var/cache/man:/bin/sh lp:x:7:7:lp:/var/spool/lpd:/bin/sh mail:x:8:8:mail:/var/mail:/bin/sh news:x:9:9:news:/var/spool/news:/bin/sh uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh proxy:x:13:13:proxy:/bin:/bin/sh www-data:x:33:33:www-data:/var/www:/bin/sh backup:x:34:34:backup:/var/backups:/bin/sh list:x:38:38:Mailing List Manager:/var/list:/bin/sh irc:x:39:39:ircd:/var/run/ircd:/bin/sh gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh nobody:x:65534:65534:nobody:/nonexistent:/bin/sh libuuid:x:100:101::/var/lib/libuuid:/bin/sh syslog:x:101:103::/home/syslog:/bin/false messagebus:x:102:105::/var/run/dbus:/bin/false avahi-autoipd:x:103:108:Avahi autoip daemon,,,:/var/lib/avahi-autoipd:/bin/false avahi:x:104:109:Avahi mDNS daemon,,,:/var/run/avahi-daemon:/bin/false usbmux:x:105:46:usbmux daemon,,,:/home/usbmux:/bin/false kernoops:x:108:65534:Kernel Oops Tracking Daemon,,,:/:/bin/false pulse:x:109:116:PulseAudio daemon,,,:/var/run/pulse:/bin/false rtkit:x:110:119:RealtimeKit,,,:/proc:/bin/false hplip:x:111:7:HPLIP system user,,,:/var/run/hplip:/bin/false saned:x:112:121::/home/saned:/bin/false whoopsie-0.2.24.5/src/tests/Makefile0000664000000000000000000000657012220367022014064 0ustar VERSION=$(shell dpkg-parsechangelog -l../../debian/changelog | grep ^Version: | cut -d' ' -f2) CFLAGS=$(shell pkg-config --cflags gio-2.0 glib-2.0 libcurl) \ $(shell libgcrypt-config --cflags) \ -g -I../../lib -I.. -DVERSION=\"$(VERSION)\" -DTEST -DTEST_DIR=\"$(CURDIR)\" -Wall LIBS=$(shell pkg-config --libs gio-2.0 glib-2.0 libcurl) -lcap \ $(shell libgcrypt-config --libs) test_parse_report_SOURCES=test_parse_report.c \ ../whoopsie.c \ ../logging.c \ ../utils.c \ ../identifier.c \ ../../lib/bson/bson.c \ ../../lib/bson/encoding.c \ ../../lib/bson/numbers.c test_parse_report_EXECUTABLE=test_parse_report test_parse_report_OBJECTS=$(test_parse_report_SOURCES:.c=.test.o) test_utils_SOURCES=test_utils.c \ ../utils.c test_utils_EXECUTABLE=test_utils test_utils_OBJECTS=$(test_utils_SOURCES:.c=.test.o) test_monitor_SOURCES=test_monitor.c \ ../monitor.c \ ../logging.c \ ../utils.c test_monitor_EXECUTABLE=test_monitor test_monitor_OBJECTS=$(test_monitor_SOURCES:.c=.test.o) test_identifier_SOURCES=test_identifier.c \ ../identifier.c test_identifier_EXECUTABLE=test_identifier test_identifier_OBJECTS=$(test_identifier_SOURCES:.c=.test.o) test_logging_SOURCES=test_logging.c \ ../logging.c test_logging_EXECUTABLE=test_logging test_logging_OBJECTS=$(test_logging_SOURCES:.c=.test.o) .PHONY: all clean check all: check check: $(test_parse_report_EXECUTABLE) $(test_utils_EXECUTABLE) $(test_monitor_EXECUTABLE) $(test_identifier_EXECUTABLE) $(test_logging_EXECUTABLE) $(foreach p, $^, ./$p -k;) $(test_parse_report_EXECUTABLE): $(test_parse_report_OBJECTS) $(CC) -std=c99 $^ $(LIBS) -o $@ $(test_utils_EXECUTABLE): $(test_utils_OBJECTS) $(CC) -std=c99 $^ $(LIBS) -o $@ $(test_monitor_EXECUTABLE): $(test_monitor_OBJECTS) $(CC) -std=c99 $^ $(LIBS) -o $@ $(test_identifier_EXECUTABLE): $(test_identifier_OBJECTS) $(CC) -std=c99 $^ $(LIBS) -o $@ $(test_logging_EXECUTABLE): $(test_logging_OBJECTS) $(CC) -std=c99 $^ $(LIBS) -o $@ test_parse_report_coverage: $(test_parse_report_OBJECTS:.test.o=.coverage.o) $(CC) -std=c99 $^ $(LIBS) -lgcov -o $@ test_utils_coverage: $(test_utils_OBJECTS:.test.o=.coverage.o) $(CC) -std=c99 $^ $(LIBS) -lgcov -o $@ test_monitor_coverage: $(test_monitor_OBJECTS:.test.o=.coverage.o) $(CC) -std=c99 $^ $(LIBS) -lgcov -o $@ test_identifier_coverage: $(test_identifier_OBJECTS:.test.o=.coverage.o) $(CC) -std=c99 $^ $(LIBS) -lgcov -o $@ test_logging_coverage: $(test_logging_OBJECTS:.test.o=.coverage.o) $(CC) -std=c99 $^ $(LIBS) -lgcov -o $@ coverage: test_parse_report_coverage test_utils_coverage test_monitor_coverage test_identifier_coverage test_logging_coverage $(foreach p, $^, ./$p -k;) $(foreach p, $(wildcard ../*.c), gcov $p -o $(p:.c=.coverage.o);) clean: rm -f $(test_parse_report_EXECUTABLE) \ $(test_parse_report_OBJECTS) \ $(test_utils_OBJECTS) \ $(test_utils_EXECUTABLE) \ $(test_monitor_OBJECTS) \ $(test_monitor_EXECUTABLE) \ $(test_identifier_OBJECTS) \ $(test_identifier_EXECUTABLE) \ $(test_logging_EXECUTABLE) \ test_parse_report_coverage \ test_utils_coverage \ test_identifier_coverage \ test_logging_coverage \ coverage find ../.. \( -name '*.coverage.o' -o \ -name '*.gcda' -o \ -name '*.gcno' -o \ -name '*.gcov' \) -delete %.coverage.o: %.c $(CC) -std=c99 -c -o $@ $^ $(CFLAGS) --coverage -O0 %.test.o: %.c $(CC) -std=c99 -c -o $@ $^ $(CFLAGS) whoopsie-0.2.24.5/src/tests/test_identifier.c0000664000000000000000000001175712220367022015754 0ustar /* whoopsie * * Copyright © 2011-2013 Canonical Ltd. * Author: Evan Dandrea * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #define _XOPEN_SOURCE #define _GNU_SOURCE #include #include #include #include #include #include #include #include "../src/identifier.h" static void test_hex_to_char (void) { char buf[9]; whoopsie_hex_to_char (buf, "\xFF\xFF\xFF\xFF", 4); g_assert_cmpstr (buf, ==, "ffffffff"); } static void test_get_mac_address (void) { char* res = NULL; GError* error = NULL; int fp = 0; char buf[18]; GDir *net_dir; const gchar *device; gchar *path; gboolean found = FALSE; whoopsie_identifier_get_mac_address (&res, &error); g_assert (res != NULL); g_assert (error == NULL); /* ensure the MAC address matches any of the system's interfaces */ net_dir = g_dir_open ("/sys/class/net", 0, NULL); if (net_dir == NULL) { g_print ("Could not open /sys/class/net\n"); g_test_fail (); goto out; } while ((device = g_dir_read_name (net_dir)) != NULL) { /* ignore loopback device */ if (strcmp (device, "lo") == 0) continue; path = g_build_filename ("/sys/class/net", device, "address", NULL); fp = open (path, O_RDONLY); g_free (path); if (fp < 0) { g_print ("Could not open /sys/class/net/%s/address\n", device); g_test_fail (); goto out; } if (read (fp, buf, 17) < 17) { g_print ("Could not read /sys/class/net/%s/address\n", device); g_test_fail (); goto out; } buf[17] = '\0'; if (g_strcmp0 (buf, res) == 0) { found = TRUE; break; } } if (!found) { g_print ("MAC address does not match any value in /sys/class/net/*/address\n"); g_test_fail (); } out: if (net_dir != NULL) g_dir_close (net_dir); if (fp >= 0) close (fp); g_free (res); } static void test_get_system_uuid (void) { /* DEADBEEF-1234-1234-1234-DEADBEEF1234 */ char* res = NULL; whoopsie_identifier_get_system_uuid (&res, NULL); if (getuid () != 0) { g_print ("Need root to run this complete test: "); goto out; } if (res == NULL) { g_test_fail (); goto out; } if (strlen (res) != 36) { g_test_fail (); goto out; } if ((res[8] != '-' || res[13] != '-') || (res[18] != '-' || res[23] != '-')) { g_test_fail (); goto out; } out: if (res) g_free (res); return; } static void test_sha512 (void) { char res[HASHLEN + 1] = {0}; // "foo" -> sha512 const char* expected = "f7fbba6e0636f890e56fbbf3283e524c6fa3204ae298382" "d624741d0dc6638326e282c41be5e4254d8820772c5518a" "2c5a8c0c7f7eda19594a7eb539453e1ed7"; whoopsie_identifier_sha512 ("foo", res, NULL); if (strcmp (res, expected) != 0) g_test_fail (); } static void test_identifier_generate (void) { char* result = NULL; char* expected = NULL; char hashed[HASHLEN + 1]; whoopsie_identifier_generate (&result, NULL); if (getuid () != 0) { whoopsie_identifier_get_mac_address (&expected, NULL); if (!expected) whoopsie_identifier_get_system_uuid (&expected, NULL); } else { whoopsie_identifier_get_system_uuid (&expected, NULL); } whoopsie_identifier_sha512 (expected, hashed, NULL); if (strcmp (result, hashed) != 0) g_test_fail (); g_free (result); g_free (expected); } int main (int argc, char** argv) { #if GLIB_MAJOR_VERSION <= 2 && GLIB_MINOR_VERSION < 35 /* Deprecated in glib 2.35/2.36. */ g_type_init (); #endif g_test_init (&argc, &argv, NULL); /* This wont work when running under fakeroot. */ if (!g_getenv ("FAKEROOTKEY")) g_test_add_func ("/whoopsie/get-system-uuid", test_get_system_uuid); g_test_add_func ("/whoopsie/hex-to-char", test_hex_to_char); g_test_add_func ("/whoopsie/get-mac-address", test_get_mac_address); g_test_add_func ("/whoopsie/sha512", test_sha512); g_test_add_func ("/whoopsie/identifier-generate", test_identifier_generate); /* Do not consider warnings to be fatal. */ g_log_set_always_fatal (G_LOG_FATAL_MASK); return g_test_run (); } whoopsie-0.2.24.5/src/tests/test_parse_report.c0000664000000000000000000003377212220367022016340 0ustar /* whoopsie * * Copyright © 2011-2013 Canonical Ltd. * Author: Evan Dandrea * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #define _XOPEN_SOURCE #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "../src/whoopsie.h" static void test_parse_report (void) { int fd; char template[12] = "/tmp/XXXXXX"; char* command[4] = {"/usr/bin/base64", "-d", NULL, NULL}; char* core = NULL; int val = -1; GHashTable* report = NULL; int i = 0; gpointer key = NULL; char* keys[] = { "ProblemType", "Architecture", "Package", "Date", "DistroRelease", "ExecutablePath", "ProcCmdline", "ProcCwd", "ProcEnviron", "ProcMaps", "ProcStatus", "Signal", "Uname", "UserGroups", "CoreDump", NULL, }; report = parse_report (TEST_DIR "/data/_usr_bin_gedit.1000.crash", FALSE, NULL); g_assert (report != NULL); while (keys[i] != NULL) { key = NULL; key = g_hash_table_lookup (report, keys[i]); if (key == NULL) g_error ("%s was not found.", keys[i]); i++; } fd = mkstemp (template); core = g_hash_table_lookup (report, "CoreDump"); write (fd, core, strlen(core)); close (fd); command[2] = template; g_spawn_sync (NULL, command, NULL, G_SPAWN_STDOUT_TO_DEV_NULL, NULL, NULL, NULL, NULL, &val, NULL); if (val != 0) { g_test_fail (); } unlink (template); g_hash_table_foreach (report, destroy_key_and_value, NULL); g_hash_table_destroy (report); } static void test_get_crash_db_url (void) { char* url = NULL; setenv ("CRASH_DB_URL", "http://localhost:8080", 1); url = get_crash_db_url (); if (g_strcmp0 (url, "http://localhost:8080")) { g_test_fail (); } else { g_free (url); url = NULL; } unsetenv ("CRASH_DB_URL"); url = get_crash_db_url (); if (url != NULL) { g_test_fail (); g_free (url); url = NULL; } setenv ("CRASH_DB_URL", "http://", 1); url = get_crash_db_url (); if (url != NULL) { g_test_fail (); g_free (url); } setenv ("CRASH_DB_URL", "httpcolonslashslash", 1); url = get_crash_db_url (); if (url != NULL) { g_test_fail (); g_free (url); } unsetenv ("CRASH_DB_URL"); } static void test_valid_report_empty_value (void) { GHashTable* report = NULL; report = parse_report (TEST_DIR "/data/crash/valid_empty_value", FALSE, NULL); if (report == NULL) g_test_fail (); else if (strcmp (g_hash_table_lookup (report, "ProblemType"), "Crash")) g_test_fail (); else if (strcmp (g_hash_table_lookup (report, "ProcMaps"), "004...")) g_test_fail (); else if (strcmp (g_hash_table_lookup (report, "ProcEnviron"), "")) g_test_fail (); if (report) { g_hash_table_foreach (report, destroy_key_and_value, NULL); g_hash_table_destroy (report); } } static void test_valid_report (void) { GHashTable* report = NULL; report = parse_report (TEST_DIR "/data/crash/valid", FALSE, NULL); const char* environ = "LANGUAGE=en_US:en\n" "LC_CTYPE=en_US.UTF-8\n" "LC_COLLATE=en_US.UTF-8\n" "PATH=(custom, user)\n" "LANG=en_US.UTF-8\n" "LC_MESSAGES=en_US.UTF-8\n" "SHELL=/bin/bash"; if (report == NULL) g_test_fail (); else if (strcmp (g_hash_table_lookup (report, "ProblemType"), "Crash")) g_test_fail (); else if (strcmp (g_hash_table_lookup (report, "Architecture"), "amd64")) g_test_fail (); else if (strcmp (g_hash_table_lookup (report, "Date"), "Thu Feb 2 17:09:31 2012")) g_test_fail (); else if (strcmp (g_hash_table_lookup (report, "ProcEnviron"), environ)) g_test_fail (); else if (strcmp (g_hash_table_lookup (report, "Uname"), "Linux 3.2.0-12-generic x86_64")) g_test_fail (); if (report) { g_hash_table_foreach (report, destroy_key_and_value, NULL); g_hash_table_destroy (report); } } static void test_embedded_carriage_return (void) { GHashTable* report = NULL; report = parse_report (TEST_DIR "/data/crash/invalid_value2", FALSE, NULL); const char* result = "LANGUAGE=en_US:en\n" "LC_CTYPE?=en_US.UTF-8\n" "LC_COLLATE=en_US.UTF-8\n" "PATH=(custom, user)\n" "LANG=en_US.UTF-8\n" "LC_MESSAGES=en_US.UTF-8\n" "SHELL=/bin/bash"; if (report == NULL) g_test_fail (); else if (strcmp (g_hash_table_lookup (report, "ProcEnviron"), result)) g_test_fail (); if (report) { g_hash_table_foreach (report, destroy_key_and_value, NULL); g_hash_table_destroy (report); } } static void test_embedded_carriage_return2 (void) { GHashTable* report = NULL; report = parse_report (TEST_DIR "/data/crash/invalid_key_special_chars", FALSE, NULL); if (report == NULL) g_test_fail (); else if (strcmp (g_hash_table_lookup (report, "ProblemType"), "Cr?ash")) g_test_fail (); if (report) { g_hash_table_foreach (report, destroy_key_and_value, NULL); g_hash_table_destroy (report); } } static void test_no_newline (void) { const char* environ = "LANGUAGE=en_US:en\n" "LC_CTYPE=en_US.UTF-8\n" "LC_COLLATE=en_US.UTF-8\n" "PATH=(custom, user)\n" "LANG=en_US.UTF-8\n" "LC_MESSAGES=en_US.UTF-8\n" "SHELL=/bin/bash"; GHashTable* report = NULL; report = parse_report (TEST_DIR "/data/crash/invalid_trailing2", FALSE, NULL); if (report == NULL) g_test_fail (); else if (strcmp (g_hash_table_lookup (report, "ProcEnviron"), environ)) g_test_fail (); if (report) { g_hash_table_foreach (report, destroy_key_and_value, NULL); g_hash_table_destroy (report); } } static void test_key_embedded_nul (void) { /* Apport does not escape the NULL byte, but we will. */ GHashTable* report = NULL; report = parse_report (TEST_DIR "/data/crash/invalid_key_embedded_nul", TRUE, NULL); if (report == NULL) g_test_fail (); else if (!g_hash_table_lookup (report, "Probl?emType")) g_test_fail (); if (report) { g_hash_table_foreach (report, destroy_key_and_value, NULL); g_hash_table_destroy (report); } } static void test_value_embedded_nul (void) { /* Apport does not escape the NULL byte. */ GHashTable* report = NULL; report = parse_report (TEST_DIR "/data/crash/invalid_value_embedded_nul", FALSE, NULL); if (report == NULL) g_test_fail (); else if (strcmp (g_hash_table_lookup (report, "ProblemType"), "Cr?ash")) g_test_fail (); if (report) { g_hash_table_foreach (report, destroy_key_and_value, NULL); g_hash_table_destroy (report); } } static void test_drop_privileges (void) { GError* err = NULL; if (getuid () != 0) { drop_privileges (&err); if (!err) g_test_fail (); else if (!g_str_has_prefix (err->message, "You must be root to run this program")) g_test_fail (); } else { drop_privileges (&err); if (getuid () == 0) g_test_fail (); } if (err) g_error_free (err); } static void test_report_expect_error (gconstpointer user_data) { const char* const* path_and_error_msg = user_data; GHashTable* report = NULL; GError* err = NULL; report = parse_report (path_and_error_msg[0], FALSE, &err); if (report) g_test_fail (); if (!err) g_test_fail (); if (err && !g_str_has_suffix (err->message, path_and_error_msg[1])) g_test_fail (); if (report) { g_hash_table_foreach (report, destroy_key_and_value, NULL); g_hash_table_destroy (report); } if (err) g_error_free (err); } static void test_unicode () { GHashTable* report = NULL; report = parse_report (TEST_DIR "/data/crash/unicode", FALSE, NULL); if (report == NULL) g_test_fail (); else if (g_strcmp0 (g_hash_table_lookup (report, "ProcCmdline"), "gedit ♥")) g_test_fail (); if (report) { g_hash_table_foreach (report, destroy_key_and_value, NULL); g_hash_table_destroy (report); } } static void test_process_existing (void) { int fd; int fd1; int fd2; char report_dir[12] = "/tmp/XXXXXX"; char* crash_file = NULL; char* upload_file = NULL; char* uploaded_file = NULL; struct stat before_uploaded_stat; struct stat after_uploaded_stat; mkdtemp (report_dir); crash_file = g_strdup_printf ("%s/fake.crash", report_dir); upload_file = g_strdup_printf ("%s/fake.upload", report_dir); uploaded_file = g_strdup_printf ("%s/fake.uploaded", report_dir); fd = creat (crash_file, 0600); fd1 = creat (upload_file, 0600); sleep (1); fd2 = creat (uploaded_file, 0600); close (fd); close (fd1); close (fd2); stat (uploaded_file, &before_uploaded_stat); sleep (1); process_existing_files (report_dir); stat (uploaded_file, &after_uploaded_stat); /* Process existing should not modify the .uploaded file LP: #1084311 */ if (before_uploaded_stat.st_mtime != after_uploaded_stat.st_mtime) { g_test_fail (); } unlink (crash_file); g_free (crash_file); unlink (upload_file); g_free (upload_file); unlink (uploaded_file); g_free (uploaded_file); rmdir (report_dir); } int main (int argc, char** argv) { #if GLIB_MAJOR_VERSION <= 2 && GLIB_MINOR_VERSION < 35 /* Deprecated in glib 2.35/2.36. */ g_type_init (); #endif g_test_init (&argc, &argv, NULL); g_test_add_func ("/whoopsie/parse-report", test_parse_report); g_test_add_func ("/whoopsie/get-crash-db-url", test_get_crash_db_url); g_test_add_func ("/whoopsie/key-embedded-nul", test_key_embedded_nul); g_test_add_func ("/whoopsie/value-embedded-nul", test_value_embedded_nul); g_test_add_func ("/whoopsie/embedded-carriage-return", test_embedded_carriage_return); g_test_add_func ("/whoopsie/embedded-carriage-return2", test_embedded_carriage_return2); g_test_add_func ("/whoopsie/valid-report", test_valid_report); g_test_add_func ("/whoopsie/valid-report-empty-value", test_valid_report_empty_value); g_test_add_func ("/whoopsie/no-newline", test_no_newline); g_test_add_func ("/whoopsie/unicode", test_unicode); g_test_add_func ("/whoopsie/process-existing", test_process_existing); const char* key_no_value_data[] = { TEST_DIR "/data/crash/invalid_key_no_value", "Report key must have a value." }; const char* key_no_value_data2[] = { TEST_DIR "/data/crash/invalid_key_no_value2", "Report key must have a value." }; const char* symlink_data[] = { TEST_DIR "/data/crash/invalid_symlink", " is a symlink or is not a regular file." }; const char* dir_data[] = { TEST_DIR "/data/crash/invalid-file-is-dir", " is a symlink or is not a regular file." }; const char* leading_spaces_data[] = { TEST_DIR "/data/crash/invalid_key_leading_spaces", "Report may not start with a value." }; const char* leading_spaces_data2[] = { TEST_DIR "/data/crash/invalid_key_leading_spaces2", "Report may not start with a value." }; const char* no_spaces_data[] = { TEST_DIR "/data/crash/invalid_no_spaces", "Report key must have a value." }; const char* empty_line_data[] = { TEST_DIR "/data/crash/invalid_value", "Report key must have a value." }; g_test_add_data_func ("/whoopsie/invalid_symlink", symlink_data, test_report_expect_error); g_test_add_data_func ("/whoopsie/invalid_directory", dir_data, test_report_expect_error); g_test_add_data_func ("/whoopsie/key-no-value", key_no_value_data, test_report_expect_error); g_test_add_data_func ("/whoopsie/key-no-value2", key_no_value_data2, test_report_expect_error); g_test_add_data_func ("/whoopsie/key-leading-spaces", leading_spaces_data, test_report_expect_error); g_test_add_data_func ("/whoopsie/key-leading-spaces2", leading_spaces_data2, test_report_expect_error); g_test_add_data_func ("/whoopsie/no-spaces", no_spaces_data, test_report_expect_error); g_test_add_data_func ("/whoopsie/empty-line", empty_line_data, test_report_expect_error); /* Run this last, so as to not mess with other tests. */ g_test_add_func ("/whoopsie/drop-privileges", test_drop_privileges); /* Do not consider warnings to be fatal. */ g_log_set_always_fatal (G_LOG_FATAL_MASK); return g_test_run (); } whoopsie-0.2.24.5/src/identifier.h0000664000000000000000000000055612061665776013577 0ustar void whoopsie_identifier_generate (char** res, GError** error); void whoopsie_identifier_get_mac_address (char** res, GError** error); void whoopsie_identifier_get_system_uuid (char** res, GError** error); void whoopsie_identifier_sha512 (char* source, char* res, GError** error); void whoopsie_hex_to_char (char* buf, const char *str, int len); #define HASHLEN 128 whoopsie-0.2.24.5/debian/0000775000000000000000000000000012321555753011721 5ustar whoopsie-0.2.24.5/debian/changelog0000664000000000000000000005260412321555725013601 0ustar whoopsie (0.2.24.5) trusty; urgency=medium * Do not send Stacktrace, and ThreadStacktrace as retracing with ddebs will provide more useful versions of them. (LP: #1306175) -- Brian Murray Thu, 10 Apr 2014 10:58:39 -0700 whoopsie (0.2.24.4) trusty; urgency=low * Close and reopen the log file so that the file descriptor is not closed when daemonizing. (LP: #1245524) -- Brian Murray Tue, 21 Jan 2014 08:23:30 -0800 whoopsie (0.2.24.3) trusty; urgency=medium * Don't use valgrind altogether, reporting unrelated leakage. -- Dimitri John Ledkov Mon, 23 Dec 2013 20:15:10 +0000 whoopsie (0.2.24.2) trusty; urgency=medium * Don't use valgrind on ppc64el. -- Matthias Klose Wed, 18 Dec 2013 17:16:25 +0100 whoopsie (0.2.24.1) saucy; urgency=low * Fix !arm64 build. -- Dmitrijs Ledkovs Wed, 16 Oct 2013 09:53:22 +0100 whoopsie (0.2.24) saucy; urgency=low * Do not run valgrind tests, when not available. -- Dmitrijs Ledkovs Wed, 16 Oct 2013 09:40:03 +0100 whoopsie (0.2.23) saucy; urgency=low * Don't lower the refcount to 0 on the system bus until shutdown, when the application is done using it (LP: #1211417). * Clean up calls to g_variant_get. Don't bother sinking the reference before calling our own functions. * Fix test_get_system_uuid unit test leaking memory. -- Evan Dandrea Fri, 16 Aug 2013 14:54:08 +0100 whoopsie (0.2.22) saucy; urgency=low * Add an option to assume there is always a route to $CRASH_DB_URL. -- Evan Dandrea Thu, 15 Aug 2013 08:35:13 +0100 whoopsie (0.2.21) saucy; urgency=low * Fix logically dead code path. Thanks Coverity (CID 989205). * Don't leak a socket if we cannot get the list of network interfaces. Thanks Coverity (CID 971184). * Avoid a potential null pointer dereference. * Always log messages via the logging code, never straight to stdout. -- Evan Dandrea Wed, 14 Aug 2013 14:02:26 +0100 whoopsie (0.2.20) saucy; urgency=low * Don't fail if the temporary file in the logging test has already gone away. -- Evan Dandrea Mon, 12 Aug 2013 09:43:25 +0100 whoopsie (0.2.19) saucy; urgency=low * Better handle stat calls in logging test. * If APPORT_REPORT_DIR is set to a nul-byte, fall back to the default. -- Evan Dandrea Fri, 09 Aug 2013 16:46:20 +0100 whoopsie (0.2.18) saucy; urgency=low * Debugging why the buildds cannot remove the temporary files created for the logging tests. -- Evan Dandrea Fri, 09 Aug 2013 15:57:54 +0100 whoopsie (0.2.17) saucy; urgency=low * Log to syslog. -- Evan Dandrea Fri, 09 Aug 2013 11:34:05 +0100 whoopsie (0.2.16) saucy; urgency=low * Continue to process the existing crash reports every two hours, not just once after two hours (LP: #1205374). -- Evan Dandrea Fri, 26 Jul 2013 16:17:31 +0100 whoopsie (0.2.15) raring; urgency=low * Bring back old GDBus code to talk to NetworkManager as a workaround for LP: #1124330. -- Evan Dandrea Tue, 26 Feb 2013 10:51:11 +0000 whoopsie (0.2.14) raring; urgency=low * Don't crash when there are no active connections. Thanks Ben Collins. -- Evan Dandrea Wed, 20 Feb 2013 18:17:21 +0000 whoopsie (0.2.13ubuntu1) raring; urgency=low * Do not start whoopsie if ubiquity presence is detected, as it causes failures to install/oem-configure. Regression between 0.2.9 - 0.2.13. See http://pad.lv/1123798 for history. -- Dmitrijs Ledkovs Mon, 18 Feb 2013 16:40:24 +0000 whoopsie (0.2.13) raring; urgency=low * Do not leak when CURL cannot be initialised. Tear down the directory monitors in the test cases. Cast to correct arguments when setting up a report processing timeout. * Don't try to connect signals to NULL objects. * Don't look for reports in /var/crash when running the valgrind tests. Makefile * Set up /var/crash earlier and with the correct permissions, while we still have root. -- Evan Dandrea Tue, 12 Feb 2013 15:12:02 +0000 whoopsie (0.2.12) raring; urgency=low * g_type_init is deprecated in 2.35/2.36 according to the buildds. * Put src/ in the library path when running whoopsie under the valgrind test. * Fix memory leak when the .upload file does not exist when checking to see if a report has already been handled. Thanks Colin Watson. * Use glib memory allocation everywhere. * Fix a leak if we cannot get the default network monitor. * Don't initialise the W32 bits in CURL. * Don't initialise curl until we're ready to use it. * Actually pass the report directory to process_existing_files at the timeout interval. -- Evan Dandrea Tue, 05 Feb 2013 17:12:18 +0000 whoopsie (0.2.11) raring; urgency=low * Build-depend on valgrind. -- Evan Dandrea Tue, 29 Jan 2013 20:23:54 +0000 whoopsie (0.2.10) quantal; urgency=low * smatch-detected redundant null check. 'If ptr is NULL, no operation is performed.' * valgrind-discovered leak in process_existing_files when the crash report has already been handled. * Clean up after the directory monitor when we're done with it. * Stop monitoring for connectivity on shutdown. * Use libnm-glib instead of DBus for communicating with NetworkManager. This fixes a hard-to-trace memory leak in our usage of gvariant. * Fix a socket leak found by Coverity. * Finally check the mkdir call for the /var/crash directory, now that Coverity is complaining about it. -- Evan Dandrea Tue, 29 Jan 2013 20:20:04 +0000 whoopsie (0.2.9) raring; urgency=low * Do not modify the .uploaded file corresponding to a .crash file when starting up (LP: #1084311) -- Brian Murray Tue, 11 Dec 2012 10:16:56 -0800 whoopsie (0.2.8) raring; urgency=low * Fix /whoopsie/get-mac-address test. This test previously hardcoded "eth0" for comparing the MAC against. eth0 is not guaranteed to exist, and whoopsie might use another device instead (such as wlan0). Change the test to require that whoopsie_identifier_get_mac_address() matches any network device instead. * Run as non-root when specifying additional environment variables. We mostly need root privileges for determining the system UUID and for creating the lock file. When testing, it is often convenient to run this with a predictable fake UUID and a temporary report directory, so start without root privileges if $CRASH_DB_IDENTIFIER is provided. Just like Apport, also respect $APPORT_REPORT_DIR to use a different report directory. If this is given, create the lock file there instead of in /var/lock/whoopsie/. * Fix all compiler warnings: - Add some missing #includes - Fix version parsing in src/tests/Makefile - Fix broken function declarations - Move non-test code into #ifndef TEST section -- Martin Pitt Tue, 11 Dec 2012 11:49:49 +0100 whoopsie (0.2.7) quantal-proposed; urgency=low * Also include the InstallationMedia field so that we can tie the installation date to a release (LP: #1071255). -- Evan Dandrea Thu, 25 Oct 2012 11:46:12 +0100 whoopsie (0.2.6) quantal-proposed; urgency=low * Send the UpgradeStatus and InstallationDate fields from apport (LP: #1070400). -- Evan Dandrea Wed, 24 Oct 2012 11:05:50 +0100 whoopsie (0.2.5) quantal; urgency=low * Send the OopsText field so that we can generate a crash signature for KernelOops problems. This lets us bucket KernelOops problems together so they appear in the 'most common problems' table on https://errors.ubuntu.com. -- Evan Dandrea Fri, 12 Oct 2012 15:25:22 +0100 whoopsie (0.2.4) quantal; urgency=low * Send the Tags field with the report so that we can create a view of just crashes of packages installed from $release-proposed. -- Evan Dandrea Thu, 11 Oct 2012 14:36:51 +0100 whoopsie (0.2.3) quantal; urgency=low * Do not send the kernel core dump (VmCore) with the initial crash metadata. -- Evan Dandrea Mon, 17 Sep 2012 10:30:19 +0100 whoopsie (0.2.2) quantal; urgency=low [ Brian Murray ] * Add LiveMediaBuild to acceptable fields (LP: #999816) -- Evan Dandrea Wed, 29 Aug 2012 10:00:56 +0100 whoopsie (0.2.1) quantal; urgency=low * Provide a libwhoopsie library. This allows the control center panel via activity-log-manager to provide a 'Show Previous Reports' button. -- Evan Dandrea Mon, 18 Jun 2012 09:32:46 +0100 whoopsie (0.2.0) quantal; urgency=low * Create the /var/metrics directory for metrics submission. Thanks Ted Gould. * Fallback to using the SHA-512 hash of the first non-loopback MAC address (LP: #963007, LP: #959308). -- Evan Dandrea Thu, 14 Jun 2012 20:57:31 +0100 whoopsie-daisy (0.1.32) precise; urgency=low * Fix failing tests on powerpc and ARM. -- Evan Dandrea Wed, 18 Apr 2012 13:04:36 +0100 whoopsie-daisy (0.1.31) precise; urgency=low * == is a bashism. -- Evan Dandrea Thu, 12 Apr 2012 15:06:11 +0100 whoopsie-daisy (0.1.30) precise; urgency=low * Stop rejecting legal arguments to the postinst (LP: #978436). Thanks Colin Watson! -- Evan Dandrea Thu, 12 Apr 2012 09:00:26 +0100 whoopsie-daisy (0.1.29) precise; urgency=low * Mark reports as complete when we get a HTTP response of 400 from the server, as these represent the server not liking what we sent it. Sending these again repeatedly would be pointless (LP: #979082). -- Evan Dandrea Wed, 11 Apr 2012 17:36:32 +0100 whoopsie-daisy (0.1.28) precise; urgency=low * Create /var/crash if it doesn't already exist. Don't silence the chmod and chgrp calls on it. Thanks Colin Watson (LP: #978502)! -- Evan Dandrea Wed, 11 Apr 2012 09:44:12 +0100 whoopsie-daisy (0.1.27) precise; urgency=low * Drop /etc/cron.daily/whoopsie. This is handled in apport now. -- Evan Dandrea Tue, 10 Apr 2012 18:01:02 +0100 whoopsie-daisy (0.1.26) precise; urgency=low * Take ownership of the NetworkManager state variant on setup and unref it, plugging a memory leak. * Log the reason the server rejected the submitted crash report. * Send the Whoopsie version with each crash submission. * Delete both .upload and .uploaded files after 14 days. Thanks Marc Deslauriers (LP: #973687). -- Evan Dandrea Tue, 10 Apr 2012 14:28:58 +0100 whoopsie-daisy (0.1.25) precise; urgency=low * Stop using a queue to monitor reports that need to be processed. Just iterate over the reports that have a .upload file, but do not have a matching .uploaded file. * Set the GSettings backend to memory to avoid pulling in DConf when we call into GNetworkMonitor. * Split out the /var/crash monitoring code and add tests for it. * Plug some memory leaks. -- Evan Dandrea Thu, 29 Mar 2012 23:30:39 +0100 whoopsie-daisy (0.1.24) precise; urgency=low * Add the file listing of /var/crash to whoopsie bug reports. Taken from apport's package hook. * Fix cppcheck call in make check. * Monitor network connectivity using NetworkManager and GNetworkMonitor. Do not report being online and able to report crashes if the user only has 3G or dial-up connectivity, or if there is no route. * Do not upload crashes multiple times if the file attributes for their .upload files change while on the report queue. -- Evan Dandrea Thu, 29 Mar 2012 11:37:51 +0100 whoopsie-daisy (0.1.23) precise; urgency=low * Fix builds failing when cppcheck isn't present. * Clean up closing file descriptors in mark_handled. Thanks Colin Watson. -- Evan Dandrea Fri, 23 Mar 2012 14:43:41 +0000 whoopsie-daisy (0.1.22) precise; urgency=low * Do not leak the file descriptor of our lockfile. * Do not leak a file descriptor when marking a report as handled. Add a test for the mark_report function. * Fix potential NULL pointer dereference in report tests. * If allocated with g_malloc, free with g_free. * Add cppcheck to make check, but only when it's installed. * Plug a memory corruption bug (hopefully). g_queue_find_custom returns a *link* not a list, so do not try to hand it back to the slice allocator as if it were a list. -- Evan Dandrea Fri, 23 Mar 2012 14:17:43 +0000 whoopsie-daisy (0.1.21) precise; urgency=low [ Steve Langasek ] * debian/rules: make sure dh_installinit knows about our upstart job, so we get correct start/stop/restart handling on install/removal/upgrade. [ Evan Dandrea ] * Disable the network detection for now, to help in tracking the source of a memory corruption bug. * Clean up option parsing a bit. -- Evan Dandrea Fri, 23 Mar 2012 11:44:51 +0000 whoopsie-daisy (0.1.20) precise; urgency=low * Handle errors in changing the filename extension, even when we're pretty sure we have an extension. * Handle errors in g_file_get_path. * CURLOPT_WRITEFUNCTION may be called successively. Grow a string with each call to account for this. * Add an apport hook for attaching the stderr output when run under upstart. -- Evan Dandrea Thu, 22 Mar 2012 15:22:51 +0000 whoopsie-daisy (0.1.19) precise; urgency=low * Handle errors in parsing the crash database URL. * Do not double-free the crash database core submission URL when a system UUID is not present (LP: #960972). * Handle errors in bson_append_string. * Handle more BSON error conditions. * Handle empty values in the apport format ("KeyName:\n") (LP: #960766, LP: #960737, LP: #960751). -- Evan Dandrea Wed, 21 Mar 2012 17:39:20 +0000 whoopsie-daisy (0.1.18) precise; urgency=low * Use https for crash reporting. * Ensure we generate core dumps when whoopsie crashes. * Do not give whoopsie a shell. -- Evan Dandrea Tue, 20 Mar 2012 21:24:51 +0000 whoopsie-daisy (0.1.17) precise; urgency=low * Ship a default configuration file. -- Evan Dandrea Fri, 16 Mar 2012 14:36:57 +0000 whoopsie-daisy (0.1.16) precise; urgency=low * Fix the build of the previous release failing on account of make check was failing in the backend code. -- Evan Dandrea Mon, 12 Mar 2012 17:45:47 +0000 whoopsie-daisy (0.1.15) precise; urgency=low * Handle multiple crashes of the same binary by watching the modification times on the .upload and .uploaded files. -- Evan Dandrea Mon, 12 Mar 2012 17:06:26 +0000 whoopsie-daisy (0.1.14) precise; urgency=low * Do not include 'base64 ' on the front of any base64-encoded field. * Check the return value of asprintf. * CURLOPT_VERBOSE expects a long. * CURLOPT_WRITEDATA expects a pointer. -- Evan Dandrea Mon, 27 Feb 2012 17:50:52 +0000 whoopsie-daisy (0.1.13) precise; urgency=low * Support UTF-8 encoded text in crash reports. * Only send a subset of the possible fields in a report, ignoring fields created by package hooks. -- Evan Dandrea Sun, 26 Feb 2012 15:44:01 +0000 whoopsie-daisy (0.1.12) precise; urgency=low * Add pyflakes to the build dependencies. -- Evan Dandrea Thu, 23 Feb 2012 15:11:59 +0000 whoopsie-daisy (0.1.11) precise; urgency=low * Do not start the daemon if crash reporting is turned off. * Output a better error message if the daemon is not started as root. * Do not keep trying to process reports that cannot be parsed. * Provide a long description for the whoopsie package. * Drop the GNOME Control Center page for controlling crash reporting. This has been moved into the activity-log-manager package and these settings can be found under the 'Diagnostics' tab in Privacy. -- Evan Dandrea Thu, 23 Feb 2012 15:01:18 +0000 whoopsie-daisy (0.1.10) precise; urgency=low * Add a cron job (run daily) to clean up the .upload and .uploaded files. * Remove the metrics preferences, since this does not exist. -- Evan Dandrea Fri, 17 Feb 2012 19:11:08 +0000 whoopsie-daisy (0.1.9) precise; urgency=low * Change the queue processing timeout to every two hours. * Change the GNOME Control Center page name to Diagnostics (LP: #934052). -- Evan Dandrea Fri, 17 Feb 2012 15:34:24 +0000 whoopsie-daisy (0.1.8) precise; urgency=low * Security fixes. Thanks Jamie Strandboge for the review. - Check the return value of the open call in get_system_uuid. - Properly initialize libcrypt. - Check that the call to gcry_md_open succeeds - Ensure that reading the SHA512 message digest succeeds. - Protect against changes to the message digest length creating a security vulnerability. - Check the returncode of setenv. - Use /var/lock/whoopsie instead of /tmp/.whoopsie-lock. - umask is usually called before fork. - Future-proof by using getrlimit instead of explicitly closing STD* - Redirect stdin, stdout, and stderr to /dev/null. - Ensure strings created in update_to_crash_file are NULL-terminated. - Only process regular files in /var/crash. - Replace calls to *alloc with g_*alloc, which calls abort() on failure. - Remove unused system_uuid pointer. - Fix warnings in make check. - Initialize all of curl. - Redirect stderr to null in chgrp and chmod calls. - Set home directory to /nonexistent. - Enable libcrypt secure memory. - Put the lock file in /var/lock/whoopsie/. - Sanity check the CRASH_DB_URL environment variable. - Added tests: - Check handling of embedded NUL bytes. - Verify that symlinks in /var/crash produce the correct error message. - Verify that keys without values in reports produce an error message. - Ensure that the report does not start with a value. - Correctly identify a report without spaces as malformed. - Verify that directories in /var/crash produce the correct error message. - Ensure that blank lines in a report are treated as errors. - Ensure that carriage returns are escaped. - Do not start multi-line values with a newline. - Check that a valid report has the exact expected contents. - Ensure that other variants of embedded carriage returns are escaped. - Verify that reports without a trailing newline are handled properly. * Change crash database URL to http://daisy.ubuntu.com. * Main inclusion request approved (LP: #913694). -- Evan Dandrea Thu, 16 Feb 2012 16:37:35 +0000 whoopsie-daisy (0.1.7) precise; urgency=low * Do not attempt to load the control center panel UI from the build directory. -- Evan Dandrea Fri, 10 Feb 2012 10:43:51 +0000 whoopsie-daisy (0.1.6) precise; urgency=low * Don't fail if there are no crash files (LP: #928735). -- Evan Dandrea Thu, 09 Feb 2012 13:18:08 +0000 whoopsie-daisy (0.1.5) precise; urgency=low * Add a control center privacy preferences page. -- Evan Dandrea Mon, 06 Feb 2012 14:19:15 +0000 whoopsie-daisy (0.1.4) precise; urgency=low * Write the system UUID to the UserOOPS ColumnFamily. * Drop the CAP_FOWNER stuff. As James points out, we can just write a .uploaded file and let cron clean up the mess. * Have the client pass the architecture, rather that have an intermediary processing step in the MQ. * Add retracing support in process_core.py. -- Evan Dandrea Thu, 26 Jan 2012 12:46:54 +0000 whoopsie-daisy (0.1.3) precise; urgency=low * Drop NetworkManager cflags and libs from Makefile. * Add missing -lcap to tests Makefile. -- Evan Dandrea Wed, 18 Jan 2012 17:58:17 +0000 whoopsie-daisy (0.1.2) precise; urgency=low * Added an upstart job. * Don't segfault if we cannot open the report. * Move to txstatsd for metrics submission. * Moved to WSGI. * Dropped Content-length, as it's superfluous. * Use oops-repository for talking to Cassandra. * Update the documentation. * Don't run install target on make. * Fix a double-free when a report is addded, then removed from the queue. * Submit the core file when asked. * Add initial MQ publishing for core file processing. * Submit a SHA-512 hash of the system UUID to key against. * Drop privileges when spawning. * Don't crash if there are files without an extension in /var/crash. * Fix a really nasty memory corruption bug. * Make warnings build failures. * Remove the lock file on exit. * Add tests for get_system_uuid, get_crash_db_url, and hex_to_char. * Move to GNetworkMonitor from NetworkManager for the network connectivity check. * Ensure in the postinst that whoopsie can read crash reports in /var/crash. * Isolate whoopsie into its own mount namespace, dropping all privileges and capabilities but CAP_FOWNER, so that we can delete files in /var/crash even though it's +t. -- Evan Dandrea Wed, 18 Jan 2012 17:26:02 +0000 whoopsie-daisy (0.1.1) precise; urgency=low * Build dependencies. -- Evan Dandrea Mon, 09 Jan 2012 08:29:06 +0000 whoopsie-daisy (0.1) precise; urgency=low * Initial Release. -- Evan Dandrea Thu, 01 Dec 2011 14:33:08 +0000 whoopsie-0.2.24.5/debian/whoopsie.maintscript0000664000000000000000000000005412061665776016044 0ustar rm_conffile /etc/cron.daily/whoopsie 0.1.25 whoopsie-0.2.24.5/debian/whoopsie.postrm0000775000000000000000000000031412061665776015035 0ustar #! /bin/sh set -e #DEBHELPER# if [ "$1" = "purge" ]; then deluser --quiet --system whoopsie > /dev/null || true chmod g-s /var/crash >/dev/null 2>&1 chgrp root /var/crash >/dev/null 2>&1 fi exit 0 whoopsie-0.2.24.5/debian/libwhoopsie0.install0000664000000000000000000000001712061665776015723 0ustar usr/lib/*.so.* whoopsie-0.2.24.5/debian/libwhoopsie-dev.install0000664000000000000000000000006512061665776016422 0ustar usr/include usr/lib/pkgconfig usr/lib/libwhoopsie.so whoopsie-0.2.24.5/debian/compat0000664000000000000000000000000212061665776013127 0ustar 9 whoopsie-0.2.24.5/debian/copyright0000664000000000000000000000300612220367022013637 0ustar Format: http://dep.debian.net/deps/dep5 Upstream-Name: whoopsie Source: http://code.launchpad.net/whoopsie Files: src/* Copyright: 2011-2013 Canonical Ltd. 2011-2013 Evan Dandrea License: GPL-3 This package is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 3 of the License. . This package is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. . You should have received a copy of the GNU General Public License along with this program. If not, see . On Debian systems, the complete text of the GNU General Public License version 3 can be found in "/usr/share/common-licenses/GPL-3". Files: lib/bson/* Copyright: 2009, 2010 10gen Inc. License: Apache-2.0 Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at . http://www.apache.org/licenses/LICENSE-2.0 . Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. whoopsie-0.2.24.5/debian/control0000664000000000000000000000230512265561220013315 0ustar Source: whoopsie Section: utils Priority: optional Maintainer: Evan Dandrea Build-Depends: debhelper (>= 9.0), libglib2.0-dev (>= 2.31.6), libcurl4-openssl-dev, libgcrypt11-dev, libcap-dev, libgtk-3-dev, network-manager-dev (>= 0.9.4.0-0ubuntu1), python, pyflakes Standards-Version: 3.9.3 Homepage: http://wiki.ubuntu.com/ErrorTracker Vcs-Bzr: http://code.launchpad.net/whoopsie Package: whoopsie Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, adduser, libwhoopsie0 (= ${binary:Version}) Pre-Depends: ${misc:Pre-Depends} Description: Ubuntu error tracker submission This program submits crash reports back to an Ubuntu server. Package: libwhoopsie0 Section: libs Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Description: Ubuntu error tracker submission - shared library This library provides methods to generate an identifier for use with the Ubuntu error tracker Package: libwhoopsie-dev Section: libdevel Architecture: any Depends: libwhoopsie0 (= ${binary:Version}), ${misc:Depends} Description: Ubuntu error tracker submission - library development files This library provides methods to generate an identifier for use with the Ubuntu error tracker whoopsie-0.2.24.5/debian/source/0000775000000000000000000000000012321555753013221 5ustar whoopsie-0.2.24.5/debian/source/format0000664000000000000000000000001512061665776014440 0ustar 3.0 (native) whoopsie-0.2.24.5/debian/rules0000775000000000000000000000105312061665776013010 0ustar #!/usr/bin/make -f # -*- makefile -*- # Sample debian/rules that uses debhelper. # This file was originally written by Joey Hess and Craig Small. # As a special exception, when this file is copied by dh-make into a # dh-make output file, you may use that output file without restriction. # This special exception was added by Craig Small in version 0.37 of dh-make. # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 export DESTDIR=$(CURDIR)/debian/whoopsie %: dh $@ override_dh_installinit: dh_installinit -pwhoopsie -o --upstart-only whoopsie-0.2.24.5/debian/whoopsie.postinst0000775000000000000000000000107012061665776015374 0ustar #! /bin/sh # postinst for whoopsie set -e if [ "$1" = configure ]; then if ! getent passwd whoopsie >/dev/null; then adduser --disabled-password --quiet --system \ --home /nonexistent --no-create-home --group whoopsie \ --shell /bin/false fi mkdir -p -m 3777 /var/crash chmod g+s /var/crash chgrp whoopsie /var/crash chgrp whoopsie /var/crash/*.crash >/dev/null 2>&1 || true chmod 0640 /var/crash/*.crash >/dev/null 2>&1 || true mkdir -p -m 3777 /var/metrics chmod g+s /var/metrics chgrp whoopsie /var/metrics fi #DEBHELPER# exit 0 whoopsie-0.2.24.5/debian/whoopsie.install0000664000000000000000000000002612061665776015154 0ustar etc usr/bin usr/share whoopsie-0.2.24.5/tools/0000775000000000000000000000000012321555753011637 5ustar whoopsie-0.2.24.5/tools/check_valgrind0000775000000000000000000000321612252132176014523 0ustar #!/bin/sh -eu working="$(mktemp -d)" check_for () { if ! grep -qs "$1" $working/vgdump; then cp "$working/vgdump" $PWD echo "Memory has leaked. Please check '$PWD/vgdump' for details." cat $PWD/vgdump exit 1 fi } if pidof whoopsie >/dev/null 2>&1; then echo "whoopsie is already running. Please stop it before continuing." >&2 exit 1 fi if [ ! -e src/whoopsie ] || [ ! -e src/tests/test_identifier ]; then echo "Please run 'make && make check' first." >&2 exit 1 fi trap "rm -rf $working" INT TERM EXIT check_result () { while ! grep -qs "definitely lost" $working/vgdump; do sleep 1 done check_for "definitely lost: 0 bytes in 0 blocks" check_for "indirectly lost: 0 bytes in 0 blocks" rm $working/vgdump } CRASH_DB_IDENTIFIER=fake-crashdb-identifier \ LD_LIBRARY_PATH=src \ CRASH_DB_URL=http://localhost:9000 \ G_SLICE=always-malloc \ G_DEBUG=gc-friendly \ valgrind \ --tool=memcheck \ --leak-check=full \ --leak-resolution=high \ --num-callers=20 \ --log-file="$working/vgdump" \ --suppressions=tools/suppressions.supp \ ./src/whoopsie -f & sleep 5 kill $! wait $! || : check_result for x in ./src/tests/test_identifier ./src/tests/test_monitor ./src/tests/test_parse_report ./src/tests/test_utils; do CRASH_DB_URL=http://localhost:9000 \ G_SLICE=always-malloc \ G_DEBUG=gc-friendly \ valgrind \ --tool=memcheck \ --leak-check=full \ --leak-resolution=high \ --num-callers=20 \ --log-file="$working/vgdump" \ --suppressions=tools/suppressions.supp \ $x check_result done exit 0 whoopsie-0.2.24.5/tools/suppressions.supp0000664000000000000000000001032712122666477015335 0ustar { NSS leak, triggered by libnm: http://git.chromium.org/gitweb/?p=chromium.git;a=blob;f=tools/valgrind/memcheck/suppressions.txt;h=d30a785d7dd51641fc0b3cd95e88a199039811b2;hb=HEAD Memcheck:Leak ... fun:NSS_NoDB_Init } { Not a leak: http://stackoverflow.com/questions/1447018/getpwnam-r-memory-leak Memcheck:Leak fun:malloc fun:nss_parse_service_list fun:__nss_database_lookup obj:* fun:getpwnam_r@@GLIBC_2.2.5 fun:getpwnam } { g_type_init Memcheck:Leak fun:malloc fun:realloc fun:g_realloc fun:type_node_any_new_W fun:g_type_register_static fun:g_pointer_type_register_static fun:g_gtype_get_type fun:_g_param_spec_types_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:calloc fun:g_malloc0 fun:type_add_flags_W fun:g_type_register_fundamental fun:_g_value_types_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:malloc fun:realloc fun:g_realloc fun:type_node_any_new_W fun:g_type_register_static fun:g_pointer_type_register_static fun:g_gtype_get_type fun:_g_param_spec_types_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:calloc fun:g_malloc0 fun:type_add_flags_W fun:g_type_register_fundamental fun:_g_value_types_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:malloc fun:realloc fun:g_realloc fun:type_add_flags_W fun:g_type_register_fundamental fun:_g_value_types_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:malloc fun:realloc fun:g_realloc fun:type_add_flags_W fun:g_type_register_fundamental fun:_g_enum_types_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:malloc fun:realloc fun:g_realloc fun:type_add_flags_W fun:g_type_register_fundamental fun:_g_boxed_type_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:malloc fun:realloc fun:g_realloc fun:type_add_flags_W fun:g_type_register_fundamental fun:_g_param_type_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:calloc fun:g_malloc0 fun:type_add_flags_W fun:g_type_register_fundamental fun:_g_enum_types_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:calloc fun:g_malloc0 fun:type_add_flags_W fun:g_type_register_fundamental fun:_g_boxed_type_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:calloc fun:g_malloc0 fun:type_add_flags_W fun:g_type_register_fundamental fun:_g_param_type_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:calloc fun:g_malloc0 fun:type_add_flags_W fun:g_type_register_fundamental fun:_g_object_type_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:calloc fun:g_malloc0 fun:type_data_make_W fun:g_type_register_fundamental fun:_g_*_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:calloc fun:g_malloc0 fun:type_node_any_new_W fun:type_node_fundamental_new_W fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:calloc fun:g_malloc0 fun:type_node_any_new_W fun:type_node_fundamental_new_W fun:g_type_register_fundamental fun:_g_*_init fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:calloc fun:g_malloc0 fun:type_data_make_W fun:g_type_init_with_debug_flags } { g_type_init Memcheck:Leak fun:realloc fun:g_realloc fun:type_node_any_new_W fun:g_type_register_static fun:g_param_type_register_static fun:_g_param_spec_types_init fun:g_type_init_with_debug_flags } { DBus singleton held by nm_client_new. dbus_g_bus_get: "The connection is a global variable shared with other callers of this function" Memcheck:Leak fun:realloc fun:g_realloc fun:g_type_set_qdata obj:* fun:dbus_g_bus_get fun:nm_client_new } whoopsie-0.2.24.5/tools/bugs_against_version.py0000664000000000000000000000127112061665776016435 0ustar #!/usr/bin/python from launchpadlib.launchpad import Launchpad import sys, os if len(sys.argv) < 2: print >>sys.stderr, 'usage: %s ' % sys.argv[0] sys.exit(1) launchpad = Launchpad.login_anonymously('whoopsie bugs by version', 'production', os.path.expanduser('~/.launchpadlib/cache')) ubuntu = launchpad.projects['ubuntu'] whoopsie = ubuntu.getSourcePackage(name='whoopsie-daisy') bugs = [] ver = sys.argv[1] for bug in whoopsie.getBugTasks(): if bug.is_complete: continue if ('\nPackage: whoopsie %s\n' % ver) in bug.bug.description: bugs.append(str(bug.bug.id)) print 'Bugs open for whoopsie %s:' % ver print ', '.join(bugs) whoopsie-0.2.24.5/tools/fetch_failed_core_dumps.py0000664000000000000000000000275112061665776017043 0ustar #!/usr/bin/python # To test Snappy against gzip, lzma, LZO, etc. from launchpadlib.launchpad import Launchpad import sys import os from gzip import GzipFile try: import cStringIO StringIO = cStringIO.StringIO except: from StringIO import StringIO if len(sys.argv) < 2: print >>sys.stderr, 'usage: %s ' % sys.argv[0] sys.exit(1) core_dumps = sys.argv[1] if not os.path.exists(core_dumps): os.makedirs(core_dumps) lp = Launchpad.login_anonymously('get failed retrace bugs', 'production') ubuntu = lp.distributions['ubuntu'] for bug in ubuntu.searchTasks(tags='apport-failed-retrace'): bug = lp.bugs[bug.bug_link] for attachment in bug.attachments: if not attachment.title == 'CoreDump.gz': continue fp = None try: fp = attachment.data.open() with open('%s/%s' % (core_dumps, bug.id), 'wb') as out: gz = None sio = None try: sio = StringIO(fp.read()) gz = GzipFile(fileobj=sio) while 1: buf = gz.read(1024) if buf: out.write(buf) else: break except MemoryError: print >>sys.stderr, "Memory error on %s" % bug.id finally: gz.close() sio.close() finally: fp.close() whoopsie-0.2.24.5/data/0000775000000000000000000000000012321555753011410 5ustar whoopsie-0.2.24.5/data/whoopsie.py0000664000000000000000000000062412061665776013631 0ustar #!/usr/bin/python from glob import glob from apport.hookutils import attach_file_if_exists, command_output def add_info(report, ui): log_file = '/var/log/upstart/whoopsie.log' attach_file_if_exists(report, log_file, 'WhoopsieLog') reports = glob('/var/crash/*') if reports: report['CrashReports'] = command_output( ['stat', '-c', '%a:%u:%g:%s:%y:%x:%n'] + reports) whoopsie-0.2.24.5/data/whoopsie0000664000000000000000000000003712061665776013200 0ustar [General] report_crashes=true whoopsie-0.2.24.5/data/whoopsie.conf0000664000000000000000000000064012122666477014121 0ustar # whoopsie - crash report submission daemon description "crash report submission daemon" start on runlevel [2345] stop on runlevel [!2345] env CRASH_DB_URL=https://daisy.ubuntu.com expect fork respawn respawn limit 10 5 pre-start script [ -x /usr/bin/ubiquity-dm ] && { stop; exit 0; } if ! grep report_crashes=true /etc/default/whoopsie -sqi; then stop; exit 0 fi end script exec whoopsie whoopsie-0.2.24.5/Makefile0000664000000000000000000000506212251727130012132 0ustar VERSION=$(shell dpkg-parsechangelog | grep ^Version: | cut -d' ' -f2) libwhoopsie_SOURCES=src/identifier.c libwhoopsie_OBJECTS=$(libwhoopsie_SOURCES:.c=.o) libwhoopsie_CFLAGS=$(shell pkg-config --cflags glib-2.0) \ $(shell libgcrypt-config --cflags) \ $(CFLAGS) $(CPPFLAGS) libwhoopsie_LIBS=$(shell pkg-config --libs glib-2.0) \ $(shell libgcrypt-config --libs) \ $(LDFLAGS) whoopsie_CFLAGS=$(shell pkg-config --cflags gio-2.0 glib-2.0 libcurl) \ -g -Ilib -Wall -Werror -Os -DVERSION=\"$(VERSION)\" \ $(CFLAGS) $(CPPFLAGS) whoopsie_LIBS=$(shell pkg-config --libs gio-2.0 glib-2.0 libcurl) -lcap \ $(LDFLAGS) SOURCES=src/whoopsie.c \ src/utils.c \ src/connectivity.c \ src/monitor.c \ src/logging.c \ lib/bson/bson.c \ lib/bson/encoding.c \ lib/bson/numbers.c OBJECTS=$(SOURCES:.c=.o) EXECUTABLE=src/whoopsie BIN=$(DESTDIR)/usr/bin DATA=$(DESTDIR)/etc .PHONY: all clean check install all: $(SOURCES) $(EXECUTABLE) $(libwhoopsie_OBJECTS): $(libwhoopsie_SOURCES) $(CC) -std=c99 -fPIC -c $(libwhoopsie_CFLAGS) -o $@ $^ src/libwhoopsie.so.0.0: $(libwhoopsie_OBJECTS) $(CC) -std=c99 -shared -Wl,-soname,libwhoopsie.so.0 -o $@ $^ $(libwhoopsie_LIBS) ln -sf libwhoopsie.so.0.0 src/libwhoopsie.so.0 ln -sf libwhoopsie.so.0.0 src/libwhoopsie.so $(EXECUTABLE): $(OBJECTS) src/libwhoopsie.so.0.0 $(CC) -std=c99 $(OBJECTS) $(whoopsie_LIBS) -Lsrc -lwhoopsie -o $@ clean: rm -f $(EXECUTABLE) $(OBJECTS) $(libwhoopsie_OBJECTS) src/libwhoopsie.so* $(MAKE) -C src/tests clean check: $(MAKE) -C src/tests if type cppcheck >/dev/null 2>&1; then \ cppcheck . --error-exitcode=1; \ fi find -name "*.py" | xargs pyflakes if type valgrind >/dev/null 2>&1; then \ APPORT_REPORT_DIR=$(shell mktemp -d) ./tools/check_valgrind; \ fi coverage: $(MAKE) -C src/tests coverage %.o: %.c $(CC) -std=c99 -c $(whoopsie_CFLAGS) -o $@ $^ install: all install -d $(BIN) install src/whoopsie $(BIN) install -d $(DATA)/init install -m644 data/whoopsie.conf $(DATA)/init install -d $(DATA)/default install -m644 data/whoopsie $(DATA)/default install -d $(DESTDIR)/usr/share/apport/package-hooks install -m644 data/whoopsie.py $(DESTDIR)/usr/share/apport/package-hooks install -d $(DESTDIR)/usr/lib install -m644 src/libwhoopsie.so.0.0 $(DESTDIR)/usr/lib cp -d src/libwhoopsie.so $(DESTDIR)/usr/lib cp -d src/libwhoopsie.so.0 $(DESTDIR)/usr/lib install -d $(DESTDIR)/usr/lib/pkgconfig install -m644 lib/libwhoopsie.pc $(DESTDIR)/usr/lib/pkgconfig install -d $(DESTDIR)/usr/include/libwhoopsie install -m644 src/identifier.h $(DESTDIR)/usr/include/libwhoopsie