778afabcb046445c5c01e71ee2812c43dab348a3
[OpenSRF.git] / src / libopensrf / utils.c
1 /*
2 Copyright (C) 2005  Georgia Public Library Service 
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU General Public License for more details.
13 */
14
15 #include <opensrf/utils.h>
16 #include <opensrf/log.h>
17 #include <errno.h>
18
19 inline void* safe_malloc( int size ) {
20         void* ptr = (void*) malloc( size );
21         if( ptr == NULL ) {
22                 osrfLogError( OSRF_LOG_MARK, "Out of Memory" );
23                 exit(99);
24         }
25         memset( ptr, 0, size ); // remove this after safe_calloc transition
26         return ptr;
27 }
28
29 inline void* safe_calloc( int size ) {
30         void* ptr = (void*) calloc( 1, size );
31         if( ptr == NULL ) {
32                 osrfLogError( OSRF_LOG_MARK, "Out of Memory" );
33                 exit(99);
34         }
35         return ptr;
36 }
37
38 /****************
39  The following static variables, and the following two functions,
40  overwrite the argv array passed to main().  The purpose is to
41  change the program name as reported by ps and similar utilities.
42
43  Warning: this code makes the non-portable assumption that the
44  strings to which argv[] points are contiguous in memory.  The
45  C Standard makes no such guarantee.
46  ****************/
47 static char** global_argv = NULL;
48 static int global_argv_size = 0;
49
50 int init_proc_title( int argc, char* argv[] ) {
51
52         global_argv = argv;
53
54         int i = 0;
55         while( i < argc ) {
56                 int len = strlen( global_argv[i]);
57                 osrf_clearbuf( global_argv[i], len );
58                 global_argv_size += len;
59                 i++;
60         }
61
62         global_argv_size -= 2;
63         return 0;
64 }
65
66 int set_proc_title( const char* format, ... ) {
67         VA_LIST_TO_STRING(format);
68         osrf_clearbuf( *(global_argv), global_argv_size);
69         return snprintf( *(global_argv), global_argv_size, VA_BUF );
70 }
71
72
73 /* utility method for profiling */
74 double get_timestamp_millis( void ) {
75         struct timeval tv;
76         gettimeofday(&tv, NULL);
77         double time     = (int)tv.tv_sec        + ( ((double)tv.tv_usec / 1000000) );
78         return time;
79 }
80
81
82 /* setting/clearing file flags */
83 int set_fl( int fd, int flags ) {
84         
85         int val;
86
87         if( (val = fcntl( fd, F_GETFL, 0) ) < 0 ) 
88                 return -1;
89
90         val |= flags;
91
92         if( fcntl( fd, F_SETFL, val ) < 0 ) 
93                 return -1;
94
95         return 0;
96 }
97         
98 int clr_fl( int fd, int flags ) {
99         
100         int val;
101
102         if( (val = fcntl( fd, F_GETFL, 0) ) < 0 ) 
103                 return -1;
104
105         val &= ~flags;
106
107         if( fcntl( fd, F_SETFL, val ) < 0 ) 
108                 return -1;
109
110         return 0;
111 }
112
113 long va_list_size(const char* format, va_list args) {
114         int len = 0;
115         len = vsnprintf(NULL, 0, format, args);
116         va_end(args);
117         len += 2;
118         return len;
119 }
120
121
122 char* va_list_to_string(const char* format, ...) {
123
124         long len = 0;
125         va_list args;
126         va_list a_copy;
127
128         va_copy(a_copy, args);
129
130         va_start(args, format);
131         len = va_list_size(format, args);
132
133         char buf[len];
134         osrf_clearbuf(buf, sizeof(buf));
135
136         va_start(a_copy, format);
137         vsnprintf(buf, len - 1, format, a_copy);
138         va_end(a_copy);
139         return strdup(buf);
140 }
141
142 // ---------------------------------------------------------------------------------
143 // Flesh out a ubiqitous growing string buffer
144 // ---------------------------------------------------------------------------------
145
146 growing_buffer* buffer_init(int num_initial_bytes) {
147
148         if( num_initial_bytes > BUFFER_MAX_SIZE ) return NULL;
149
150         size_t len = sizeof(growing_buffer);
151
152         growing_buffer* gb;
153         OSRF_MALLOC(gb, len);
154
155         gb->n_used = 0;/* nothing stored so far */
156         gb->size = num_initial_bytes;
157         OSRF_MALLOC(gb->buf, gb->size + 1);
158
159         return gb;
160 }
161
162
163 /* Expand the internal buffer of a growing_buffer so that it */
164 /* will accommodate a specified string length.  Return 0 if  */
165 /* successful, or 1 otherwise. */
166
167 /* Note that we do not check to see if the buffer is already */
168 /* big enough.  It is the responsibility of the calling      */
169 /* function to call this only when necessary. */
170
171 static int buffer_expand( growing_buffer* gb, size_t total_len ) {
172
173         // Make sure the request is not excessive
174         
175         if( total_len >= BUFFER_MAX_SIZE ) {
176                 fprintf(stderr, "Buffer reached MAX_SIZE of %lu",
177                                 (unsigned long) BUFFER_MAX_SIZE );
178                 buffer_free( gb );
179                 return 1;
180         }
181
182         // Pick a big enough buffer size, but don't exceed a maximum
183         
184         while( total_len >= gb->size ) {
185                 gb->size *= 2;
186         }
187
188         if( gb->size > BUFFER_MAX_SIZE )
189                 gb->size = BUFFER_MAX_SIZE;
190
191         // Allocate and populate the new buffer
192         
193         char* new_data;
194         OSRF_MALLOC( new_data, gb->size );
195         memcpy( new_data, gb->buf, gb->n_used );
196         new_data[ gb->n_used ] = '\0';
197
198         // Replace the old buffer
199         
200         free( gb->buf );
201         gb->buf = new_data;
202         return 0;
203 }
204
205
206 int buffer_fadd(growing_buffer* gb, const char* format, ... ) {
207
208         if(!gb || !format) return 0; 
209
210         long len = 0;
211         va_list args;
212         va_list a_copy;
213
214         va_copy(a_copy, args);
215
216         va_start(args, format);
217         len = va_list_size(format, args);
218
219         char buf[len];
220         osrf_clearbuf(buf, sizeof(buf));
221
222         va_start(a_copy, format);
223         vsnprintf(buf, len - 1, format, a_copy);
224         va_end(a_copy);
225
226         return buffer_add(gb, buf);
227
228 }
229
230
231 int buffer_add(growing_buffer* gb, const char* data) {
232         if(!(gb && data)) return 0;
233
234         int data_len = strlen( data );
235
236         if(data_len == 0) return 0;
237
238         int total_len = data_len + gb->n_used;
239
240         if( total_len >= gb->size ) {
241                 if( buffer_expand( gb, total_len ) )
242                         return -1;
243         }
244
245         strcpy( gb->buf + gb->n_used, data );
246         gb->n_used = total_len;
247         return total_len;
248 }
249
250
251 int buffer_reset( growing_buffer *gb){
252         if( gb == NULL ) { return -1; }
253         if( gb->buf == NULL ) { return -1; }
254         osrf_clearbuf( gb->buf, gb->size );
255         gb->n_used = 0;
256         gb->buf[ 0 ] = '\0';
257         return gb->n_used;
258 }
259
260 /* Return a pointer to the text within a growing_buffer, */
261 /* while destroying the growing_buffer itself.           */
262
263 char* buffer_release( growing_buffer* gb) {
264         char* s = gb->buf;
265         s[gb->n_used] = '\0';
266         free( gb );
267         return s;
268 }
269
270 /* Destroy a growing_buffer and the text it contains */
271
272 int buffer_free( growing_buffer* gb ) {
273         if( gb == NULL ) 
274                 return 0;
275         free( gb->buf );
276         free( gb );
277         return 1;
278 }
279
280 char* buffer_data( const growing_buffer *gb) {
281         return strdup( gb->buf );
282 }
283
284 int buffer_chomp(growing_buffer* gb) {
285         if( gb == NULL ) { return -1; }
286     if(gb->n_used > 0) {
287             gb->n_used--;
288             gb->buf[gb->n_used] = '\0';
289     }
290     return gb->n_used;
291 }
292
293
294 /*
295 #define OSRF_BUFFER_ADD_CHAR(gb, c)\
296         do {\
297                 if(gb) {\
298                         if(gb->n_used < gb->size - 1)\
299                                 gb->buf[gb->n_used++] = c;\
300                         else\
301                                 buffer_add_char(gb, c);\
302                 }\
303         }while(0)
304         */
305
306 int buffer_add_char(growing_buffer* gb, char c ) {
307         if(gb && gb->buf) {
308
309                 int total_len = gb->n_used + 1;
310
311                 if( total_len >= gb->size ) {
312                         if( buffer_expand( gb, total_len ) )
313                                 return -1;
314                 }
315         
316                 gb->buf[ gb->n_used ]   = c;
317                 gb->buf[ ++gb->n_used ] = '\0';
318         }
319         
320         return gb->n_used;
321 }
322
323
324 char* uescape( const char* string, int size, int full_escape ) {
325
326         if( NULL == string )
327                 return NULL;
328         
329         growing_buffer* buf = buffer_init(size + 64);
330         int clen = 0;
331         int idx = 0;
332         unsigned long int c = 0x0;
333
334         while (string[idx]) {
335
336                 c = 0x0;
337
338                 if ((unsigned char)string[idx] >= 0x80) { // not ASCII
339
340                         if ((unsigned char)string[idx] >= 0xC0 && (unsigned char)string[idx] <= 0xF4) { // starts a UTF8 string
341
342                                 clen = 1;
343                                 if (((unsigned char)string[idx] & 0xF0) == 0xF0) {
344                                         clen = 3;
345                                         c = (unsigned char)string[idx] ^ 0xF0;
346
347                                 } else if (((unsigned char)string[idx] & 0xE0) == 0xE0) {
348                                         clen = 2;
349                                         c = (unsigned char)string[idx] ^ 0xE0;
350
351                                 } else if (((unsigned char)string[idx] & 0xC0) == 0xC0) {
352                                         clen = 1;
353                                         c = (unsigned char)string[idx] ^ 0xC0;
354                                 }
355
356                                 for (;clen;clen--) {
357
358                                         idx++; // look at the next byte
359                                         c = (c << 6) | ((unsigned char)string[idx] & 0x3F); // add this byte worth
360
361                                 }
362
363                                 buffer_fadd(buf, "\\u%04x", c);
364
365                         } else {
366                                 buffer_free(buf);
367                                 return NULL;
368                         }
369
370                 } else {
371                         c = string[idx];
372
373                         /* escape the usual suspects */
374                         if(full_escape) {
375                                 switch(c) {
376                                         case '"':
377                                                 OSRF_BUFFER_ADD_CHAR(buf, '\\');
378                                                 OSRF_BUFFER_ADD_CHAR(buf, '"');
379                                                 break;
380         
381                                         case '\b':
382                                                 OSRF_BUFFER_ADD_CHAR(buf, '\\');
383                                                 OSRF_BUFFER_ADD_CHAR(buf, 'b');
384                                                 break;
385         
386                                         case '\f':
387                                                 OSRF_BUFFER_ADD_CHAR(buf, '\\');
388                                                 OSRF_BUFFER_ADD_CHAR(buf, 'f');
389                                                 break;
390         
391                                         case '\t':
392                                                 OSRF_BUFFER_ADD_CHAR(buf, '\\');
393                                                 OSRF_BUFFER_ADD_CHAR(buf, 't');
394                                                 break;
395         
396                                         case '\n':
397                                                 OSRF_BUFFER_ADD_CHAR(buf, '\\');
398                                                 OSRF_BUFFER_ADD_CHAR(buf, 'n');
399                                                 break;
400         
401                                         case '\r':
402                                                 OSRF_BUFFER_ADD_CHAR(buf, '\\');
403                                                 OSRF_BUFFER_ADD_CHAR(buf, 'r');
404                                                 break;
405
406                                         case '\\':
407                                                 OSRF_BUFFER_ADD_CHAR(buf, '\\');
408                                                 OSRF_BUFFER_ADD_CHAR(buf, '\\');
409                                                 break;
410
411                                         default:
412                                                 if( c < 32 ) buffer_fadd(buf, "\\u%04x", c);
413                                                 else OSRF_BUFFER_ADD_CHAR(buf, c);
414                                 }
415
416                         } else {
417                                 OSRF_BUFFER_ADD_CHAR(buf, c);
418                         }
419                 }
420
421                 idx++;
422         }
423
424         return buffer_release(buf);
425 }
426
427
428 // A function to turn a process into a daemon 
429 int daemonize( void ) {
430         pid_t f = fork();
431
432         if (f == -1) {
433                 osrfLogError( OSRF_LOG_MARK, "Failed to fork!" );
434                 return -1;
435
436         } else if (f == 0) { // We're in the child now...
437                 
438                 // Change directories.  Otherwise whatever directory
439                 // we're in couldn't be deleted until the program
440                 // terminated -- possibly causing some inconvenience.
441                 chdir( "/" );
442
443                 /* create new session */
444                 setsid();
445
446                 // Now that we're no longer attached to a terminal,
447                 // we don't want any traffic on the standard streams
448                 freopen( "/dev/null", "r", stdin );
449                 freopen( "/dev/null", "w", stdout );
450                 freopen( "/dev/null", "w", stderr );
451                 
452                 return 0;
453
454         } else { // We're in the parent...
455                 _exit(0);
456         }
457 }
458
459
460 /* Return 1 if the string represents an integer,  */
461 /* as recognized by strtol(); Otherwise return 0. */
462
463 int stringisnum(const char* s) {
464         char* w;
465         strtol(s, &w, 10);
466         return *w ? 0 : 1;
467 }
468         
469
470
471 char* file_to_string(const char* filename) {
472
473         if(!filename) return NULL;
474
475         FILE * file = fopen( filename, "r" );
476         if( !file ) {
477                 osrfLogError( OSRF_LOG_MARK, "Unable to open file [%s]", filename );
478                 return NULL;
479         }
480
481         size_t num_read;
482         char buf[ BUFSIZ + 1 ];
483         growing_buffer* gb = buffer_init(sizeof(buf));
484
485         while( ( num_read = fread( buf, 1, sizeof(buf) - 1, file) ) ) {
486                 buf[ num_read ] = '\0';
487                 buffer_add(gb, buf);
488         }
489
490         fclose(file);
491
492         return buffer_release(gb);
493 }
494
495
496 char* md5sum( const char* text, ... ) {
497
498         struct md5_ctx ctx;
499         unsigned char digest[16];
500
501         MD5_start (&ctx);
502
503         VA_LIST_TO_STRING(text);
504
505         int i;
506         for ( i=0 ; i != strlen(VA_BUF) ; i++ )
507                 MD5_feed (&ctx, VA_BUF[i]);
508
509         MD5_stop (&ctx, digest);
510
511         char buf[16];
512         char final[256];
513         osrf_clearbuf(final, sizeof(final));
514
515         for ( i=0 ; i<16 ; i++ ) {
516                 snprintf(buf, sizeof(buf), "%02x", digest[i]);
517                 strcat( final, buf );
518         }
519
520         return strdup(final);
521
522 }
523
524 int osrfUtilsCheckFileDescriptor( int fd ) {
525
526         fd_set tmpset;
527         FD_ZERO(&tmpset);
528         FD_SET(fd, &tmpset);
529
530         struct timeval tv;
531         tv.tv_sec = 0;
532         tv.tv_usec = 0;
533
534         if( select(fd + 1, &tmpset, NULL, NULL, &tv) == -1 ) {
535                 if( errno == EBADF ) return -1;
536         }
537
538         return 0;
539 }
540