Broad patch from Dan Scott to move towards better memory management:
[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 );
26         return ptr;
27 }
28
29 /****************
30  The following static variables, and the following two functions,
31  overwrite the argv array passed to main().  The purpose is to
32  change the program name as reported by ps and similar utilities.
33
34  Warning: this code makes the non-portable assumption that the
35  strings to which argv[] points are contiguous in memory.  The
36  C Standard makes no such guarantee.
37  ****************/
38 static char** global_argv = NULL;
39 static int global_argv_size = 0;
40
41 int init_proc_title( int argc, char* argv[] ) {
42
43         global_argv = argv;
44
45         int i = 0;
46         while( i < argc ) {
47                 int len = strlen( global_argv[i]);
48                 memset( global_argv[i], 0, len);
49                 global_argv_size += len;
50                 i++;
51         }
52
53         global_argv_size -= 2;
54         return 0;
55 }
56
57 int set_proc_title( char* format, ... ) {
58         VA_LIST_TO_STRING(format);
59         memset( *(global_argv), 0, global_argv_size);
60         return snprintf( *(global_argv), global_argv_size, VA_BUF );
61 }
62
63
64 /* utility method for profiling */
65 double get_timestamp_millis() {
66         struct timeval tv;
67         gettimeofday(&tv, NULL);
68         double time     = (int)tv.tv_sec        + ( ((double)tv.tv_usec / 1000000) );
69         return time;
70 }
71
72
73 /* setting/clearing file flags */
74 int set_fl( int fd, int flags ) {
75         
76         int val;
77
78         if( (val = fcntl( fd, F_GETFL, 0) ) < 0 ) 
79                 return -1;
80
81         val |= flags;
82
83         if( fcntl( fd, F_SETFL, val ) < 0 ) 
84                 return -1;
85
86         return 0;
87 }
88         
89 int clr_fl( int fd, int flags ) {
90         
91         int val;
92
93         if( (val = fcntl( fd, F_GETFL, 0) ) < 0 ) 
94                 return -1;
95
96         val &= ~flags;
97
98         if( fcntl( fd, F_SETFL, val ) < 0 ) 
99                 return -1;
100
101         return 0;
102 }
103
104 long va_list_size(const char* format, va_list args) {
105         int len = 0;
106         len = vsnprintf(NULL, 0, format, args);
107         va_end(args);
108         len += 2;
109         return len;
110 }
111
112
113 char* va_list_to_string(const char* format, ...) {
114
115         long len = 0;
116         va_list args;
117         va_list a_copy;
118
119         va_copy(a_copy, args);
120
121         va_start(args, format);
122         len = va_list_size(format, args);
123
124         char buf[len];
125         memset(buf, 0, sizeof(buf));
126
127         va_start(a_copy, format);
128         vsnprintf(buf, len - 1, format, a_copy);
129         va_end(a_copy);
130         return strdup(buf);
131 }
132
133 // ---------------------------------------------------------------------------------
134 // Flesh out a ubiqitous growing string buffer
135 // ---------------------------------------------------------------------------------
136
137 growing_buffer* buffer_init(int num_initial_bytes) {
138
139         if( num_initial_bytes > BUFFER_MAX_SIZE ) return NULL;
140
141         size_t len = sizeof(growing_buffer);
142
143         growing_buffer* gb;
144         OSRF_MALLOC(gb, len);
145
146         gb->n_used = 0;/* nothing stored so far */
147         gb->size = num_initial_bytes;
148         OSRF_MALLOC(gb->buf, gb->size + 1);
149
150         return gb;
151 }
152
153
154 int buffer_fadd(growing_buffer* gb, const char* format, ... ) {
155
156         if(!gb || !format) return 0; 
157
158         long len = 0;
159         va_list args;
160         va_list a_copy;
161
162         va_copy(a_copy, args);
163
164         va_start(args, format);
165         len = va_list_size(format, args);
166
167         char buf[len];
168         memset(buf, 0, sizeof(buf));
169
170         va_start(a_copy, format);
171         vsnprintf(buf, len - 1, format, a_copy);
172         va_end(a_copy);
173
174         return buffer_add(gb, buf);
175
176 }
177
178
179 int buffer_add(growing_buffer* gb, char* data) {
180         if(!(gb && data)) return 0;
181
182         int data_len = strlen( data );
183
184         if(data_len == 0) return 0;
185
186         int total_len = data_len + gb->n_used;
187
188         if( total_len >= gb->size ) {
189                 while( total_len >= gb->size ) {
190                         gb->size *= 2;
191                 }
192         
193                 if( gb->size > BUFFER_MAX_SIZE ) {
194                         osrfLogError( OSRF_LOG_MARK, "Buffer reached MAX_SIZE of %d", BUFFER_MAX_SIZE );
195                         buffer_free( gb );
196                         return 0;
197                 }
198         
199                 char* new_data;
200                 OSRF_MALLOC(new_data, gb->size );
201         
202                 strcpy( new_data, gb->buf );
203                 free( gb->buf );
204                 gb->buf = new_data;
205         }
206
207         strcat( gb->buf, data );
208         gb->n_used = total_len;
209         return total_len;
210 }
211
212
213 int buffer_reset( growing_buffer *gb){
214         if( gb == NULL ) { return 0; }
215         if( gb->buf == NULL ) { return 0; }
216         memset( gb->buf, 0, sizeof(gb->buf) );
217         gb->n_used = 0;
218         return 1;
219 }
220
221 /* Return a pointer to the text within a growing_buffer, */
222 /* while destroying the growing_buffer itself.           */
223
224 char* buffer_release( growing_buffer* gb) {
225         char* s = gb->buf;
226         s[gb->n_used] = '\0';
227         free( gb );
228         return s;
229 }
230
231 /* Destroy a growing_buffer and the text it contains */
232
233 int buffer_free( growing_buffer* gb ) {
234         if( gb == NULL ) 
235                 return 0;
236         free( gb->buf );
237         free( gb );
238         return 1;
239 }
240
241 char* buffer_data( growing_buffer *gb) {
242         return strdup( gb->buf );
243 }
244
245
246 /*
247 #define OSRF_BUFFER_ADD_CHAR(gb, c)\
248         do {\
249                 if(gb) {\
250                         if(gb->n_used < gb->size - 1)\
251                                 gb->buf[gb->n_used++] = c;\
252                         else\
253                                 buffer_add_char(gb, c);\
254                 }\
255         }while(0)
256         */
257
258 int buffer_add_char(growing_buffer* gb, char c) {
259         char buf[2];
260         buf[0] = c;
261         buf[1] = '\0';
262         buffer_add(gb, buf);
263         return 1;
264 }
265
266
267
268 char* uescape( const char* string, int size, int full_escape ) {
269
270         growing_buffer* buf = buffer_init(size + 64);
271         int clen = 0;
272         int idx = 0;
273         unsigned long int c = 0x0;
274
275         while (string[idx]) {
276
277                 c = 0x0;
278
279                 if ((unsigned char)string[idx] >= 0x80) { // not ASCII
280
281                         if ((unsigned char)string[idx] >= 0xC0 && (unsigned char)string[idx] <= 0xF4) { // starts a UTF8 string
282
283                                 clen = 1;
284                                 if (((unsigned char)string[idx] & 0xF0) == 0xF0) {
285                                         clen = 3;
286                                         c = (unsigned char)string[idx] ^ 0xF0;
287
288                                 } else if (((unsigned char)string[idx] & 0xE0) == 0xE0) {
289                                         clen = 2;
290                                         c = (unsigned char)string[idx] ^ 0xE0;
291
292                                 } else if (((unsigned char)string[idx] & 0xC0) == 0xC0) {
293                                         clen = 1;
294                                         c = (unsigned char)string[idx] ^ 0xC0;
295                                 }
296
297                                 for (;clen;clen--) {
298
299                                         idx++; // look at the next byte
300                                         c = (c << 6) | ((unsigned char)string[idx] & 0x3F); // add this byte worth
301
302                                 }
303
304                                 buffer_fadd(buf, "\\u%04x", c);
305
306                         } else {
307                                 buffer_free(buf);
308                                 return NULL;
309                         }
310
311                 } else {
312                         c = string[idx];
313
314                         /* escape the usual suspects */
315                         if(full_escape) {
316                                 switch(c) {
317                                         case '"':
318                                                 OSRF_BUFFER_ADD_CHAR(buf, '\\');
319                                                 OSRF_BUFFER_ADD_CHAR(buf, '"');
320                                                 break;
321         
322                                         case '\b':
323                                                 OSRF_BUFFER_ADD_CHAR(buf, '\\');
324                                                 OSRF_BUFFER_ADD_CHAR(buf, 'b');
325                                                 break;
326         
327                                         case '\f':
328                                                 OSRF_BUFFER_ADD_CHAR(buf, '\\');
329                                                 OSRF_BUFFER_ADD_CHAR(buf, 'f');
330                                                 break;
331         
332                                         case '\t':
333                                                 OSRF_BUFFER_ADD_CHAR(buf, '\\');
334                                                 OSRF_BUFFER_ADD_CHAR(buf, 't');
335                                                 break;
336         
337                                         case '\n':
338                                                 OSRF_BUFFER_ADD_CHAR(buf, '\\');
339                                                 OSRF_BUFFER_ADD_CHAR(buf, 'n');
340                                                 break;
341         
342                                         case '\r':
343                                                 OSRF_BUFFER_ADD_CHAR(buf, '\\');
344                                                 OSRF_BUFFER_ADD_CHAR(buf, 'r');
345                                                 break;
346
347                                         case '\\':
348                                                 OSRF_BUFFER_ADD_CHAR(buf, '\\');
349                                                 OSRF_BUFFER_ADD_CHAR(buf, '\\');
350                                                 break;
351
352                                         default:
353                                                 if( c < 32 ) buffer_fadd(buf, "\\u%04x", c);
354                                                 else OSRF_BUFFER_ADD_CHAR(buf, c);
355                                 }
356
357                         } else {
358                                 OSRF_BUFFER_ADD_CHAR(buf, c);
359                         }
360                 }
361
362                 idx++;
363         }
364
365         char* d = buffer_data(buf);
366         buffer_free(buf);
367         return d;
368 }
369
370
371 // A function to turn a process into a daemon 
372 int daemonize() {
373         pid_t f = fork();
374
375         if (f == -1) {
376                 osrfLogError( OSRF_LOG_MARK, "Failed to fork!" );
377                 return -1;
378
379         } else if (f == 0) { // We're in the child now...
380                 
381                 // Change directories.  Otherwise whatever directory
382                 // we're in couldn't be deleted until the program
383                 // terminated -- possibly causing some inconvenience.
384                 chdir( "/" );
385
386                 /* create new session */
387                 setsid();
388
389                 // Now that we're no longer attached to a terminal,
390                 // we don't want any traffic on the standard streams
391                 freopen( "/dev/null", "r", stdin );
392                 freopen( "/dev/null", "w", stdout );
393                 freopen( "/dev/null", "w", stderr );
394                 
395                 return 0;
396
397         } else { // We're in the parent...
398                 _exit(0);
399         }
400 }
401
402
403 /* Return 1 if the string represents an integer,  */
404 /* as recognized by strtol(); Otherwise return 0. */
405
406 int stringisnum(char* s) {
407         char* w;
408         strtol(s, &w, 10);
409         return *w ? 0 : 1;
410 }
411         
412
413
414 char* file_to_string(const char* filename) {
415
416         if(!filename) return NULL;
417
418         int len = 1024;
419         char buf[len];
420         memset(buf, 0, sizeof(buf));
421         growing_buffer* gb = buffer_init(len);
422
423         FILE* file = fopen(filename, "r");
424         if(!file) {
425                 osrfLogError( OSRF_LOG_MARK, "Unable to open file [%s]", filename );
426                 return NULL;
427         }
428
429         while(fgets(buf, sizeof(buf), file)) {
430                 buffer_add(gb, buf);
431                 memset(buf, 0, sizeof(buf));
432         }
433
434         fclose(file);
435
436         char* data = buffer_data(gb);
437         buffer_free(gb);
438         return data;
439 }
440
441
442 char* md5sum( char* text, ... ) {
443
444         struct md5_ctx ctx;
445         unsigned char digest[16];
446
447         MD5_start (&ctx);
448
449         VA_LIST_TO_STRING(text);
450
451         int i;
452         for ( i=0 ; i != strlen(VA_BUF) ; i++ )
453                 MD5_feed (&ctx, VA_BUF[i]);
454
455         MD5_stop (&ctx, digest);
456
457         char buf[16];
458         char final[256];
459         memset(final, 0, sizeof(final));
460
461         for ( i=0 ; i<16 ; i++ ) {
462                 snprintf(buf, sizeof(buf), "%02x", digest[i]);
463                 strcat( final, buf );
464         }
465
466         return strdup(final);
467
468 }
469
470 int osrfUtilsCheckFileDescriptor( int fd ) {
471
472         fd_set tmpset;
473         FD_ZERO(&tmpset);
474         FD_SET(fd, &tmpset);
475
476         struct timeval tv;
477         tv.tv_sec = 0;
478         tv.tv_usec = 0;
479
480         if( select(fd + 1, &tmpset, NULL, NULL, &tv) == -1 ) {
481                 if( errno == EBADF ) return -1;
482         }
483
484         return 0;
485 }
486