]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/util.c
c8f65699abda086051e7255fa95f8f7323567ea6
[bacula/bacula] / bacula / src / lib / util.c
1 /*
2  *   util.c  miscellaneous utility subroutines for Bacula
3  * 
4  *    Kern Sibbald, MM
5  *
6  *   Version $Id$
7  */
8
9 /*
10    Copyright (C) 2000, 2001, 2002 Kern Sibbald and John Walker
11
12    This program is free software; you can redistribute it and/or
13    modify it under the terms of the GNU General Public License as
14    published by the Free Software Foundation; either version 2 of
15    the License, or (at your option) any later version.
16
17    This program is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20    General Public License for more details.
21
22    You should have received a copy of the GNU General Public
23    License along with this program; if not, write to the Free
24    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
25    MA 02111-1307, USA.
26
27  */
28
29 #include "bacula.h"
30 #include "jcr.h"
31 #include "findlib/find.h"
32
33 /*
34  * Various Bacula Utility subroutines
35  *
36  */
37
38 /*
39  * Convert a string to btime_t (64 bit seconds)
40  * Returns 0: if error
41            1: if OK, and value stored in value
42  */
43 int string_to_btime(char *str, btime_t *value)
44 {
45    int i, ch, len;
46    double val;
47    static int  mod[] = {'*', 's', 'n', 'h', 'd', 'w', 'm', 'q', 'y', 0};
48    static int mult[] = {1,    1,  60, 60*60, 60*60*24, 60*60*24*7, 60*60*24*30, 
49                   60*60*24*91, 60*60*24*365};
50
51    /* Look for modifier */
52    len = strlen(str);
53    ch = str[len - 1];
54    i = 0;
55    if (ISALPHA(ch)) {
56       if (ISUPPER(ch)) {
57          ch = tolower(ch);
58       }
59       while (mod[++i] != 0) {
60          if (ch == mod[i]) {
61             len--;
62             str[len] = 0; /* strip modifier */
63             break;
64          }
65       }
66    }
67    if (mod[i] == 0 || !is_a_number(str)) {
68       return 0;
69    }
70    val = strtod(str, NULL);
71    if (errno != 0 || val < 0) {
72       return 0;
73    }
74    *value = (btime_t)(val * mult[i]);
75    return 1;
76
77 }
78
79 char *edit_btime(btime_t val, char *buf)
80 {
81    char mybuf[30];
82    static int mult[] = {60*60*24*365, 60*60*24*30, 60*60*24, 60*60, 60};
83    static char *mod[]  = {"year",  "month",  "day", "hour", "min"};
84    int i;
85    uint32_t times;
86
87    *buf = 0;
88    for (i=0; i<5; i++) {
89       times = val / mult[i];
90       if (times > 0) {
91          val = val - (btime_t)times * mult[i];
92          sprintf(mybuf, "%d %s%s ", times, mod[i], times>1?"s":"");
93          strcat(buf, mybuf);
94       }
95    }
96    if (val == 0 && strlen(buf) == 0) {     
97       strcat(buf, "0 secs");
98    } else if (val != 0) {
99       sprintf(mybuf, "%d sec%s", (uint32_t)val, val>1?"s":"");
100       strcat(buf, mybuf);
101    }
102    return buf;
103 }
104
105 /*
106  * Check if specified string is a number or not.
107  *  Taken from SQLite, cool, thanks.
108  */
109 int is_a_number(const char *n)
110 {
111    int digit_seen = 0;
112
113    if( *n == '-' || *n == '+' ) {
114       n++;
115    }
116    while (ISDIGIT(*n)) {
117       digit_seen = 1;
118       n++;
119    }
120    if (digit_seen && *n == '.') {
121       n++;
122       while (ISDIGIT(*n)) { n++; }
123    }
124    if (digit_seen && (*n == 'e' || *n == 'E')
125        && (ISDIGIT(n[1]) || ((n[1]=='-' || n[1] == '+') && ISDIGIT(n[2])))) {
126       n += 2;                         /* skip e- or e+ or e digit */
127       while (ISDIGIT(*n)) { n++; }
128    }
129    return digit_seen && *n==0;
130 }
131
132
133 /*
134  * Edit an integer number with commas, the supplied buffer
135  * must be at least 27 bytes long.  The incoming number
136  * is always widened to 64 bits.
137  */
138 char *edit_uint64_with_commas(uint64_t val, char *buf)
139 {
140    sprintf(buf, "%" lld, val);
141    return add_commas(buf, buf);
142 }
143
144 /*
145  * Edit an integer number, the supplied buffer
146  * must be at least 27 bytes long.  The incoming number
147  * is always widened to 64 bits.
148  */
149 char *edit_uint64(uint64_t val, char *buf)
150 {
151    sprintf(buf, "%" lld, val);
152    return buf;
153 }
154
155
156 /*
157  * Add commas to a string, which is presumably
158  * a number.  
159  */
160 char *add_commas(char *val, char *buf)
161 {
162    int len, nc;
163    char *p, *q;
164    int i;
165
166    if (val != buf) {
167       strcpy(buf, val);
168    }
169    len = strlen(buf);
170    if (len < 1) {
171       len = 1;
172    }
173    nc = (len - 1) / 3;
174    p = buf+len;
175    q = p + nc;
176    *q-- = *p--;
177    for ( ; nc; nc--) {
178       for (i=0; i < 3; i++) {
179           *q-- = *p--;
180       }
181       *q-- = ',';
182    }   
183    return buf;
184 }
185
186
187 /* Convert a string in place to lower case */
188 void lcase(char *str)
189 {
190    while (*str) {
191       if (ISUPPER(*str))
192          *str = tolower((int)(*str));
193        str++;
194    }
195 }
196
197 /* Convert spaces to non-space character. 
198  * This makes scanf of fields containing spaces easier.
199  */
200 void
201 bash_spaces(char *str)
202 {
203    while (*str) {
204       if (*str == ' ')
205          *str = 0x1;
206       str++;
207    }
208 }
209
210 /* Convert non-space characters (0x1) back into spaces */
211 void
212 unbash_spaces(char *str)
213 {
214    while (*str) {
215      if (*str == 0x1)
216         *str = ' ';
217      str++;
218    }
219 }
220
221 /* Strip any trailing junk from the command */
222 void strip_trailing_junk(char *cmd)
223 {
224    char *p;
225    p = cmd + strlen(cmd) - 1;
226
227    /* strip trailing junk from command */
228    while ((p >= cmd) && (*p == '\n' || *p == '\r' || *p == ' '))
229       *p-- = 0;
230 }
231
232 /* Strip any trailing slashes from a directory path */
233 void strip_trailing_slashes(char *dir)
234 {
235    char *p;
236    p = dir + strlen(dir) - 1;
237
238    /* strip trailing slashes */
239    while ((p >= dir) && (*p == '/'))
240       *p-- = 0;
241 }
242
243 /*
244  * Skip spaces
245  *  Returns: 0 on failure (EOF)             
246  *           1 on success
247  *           new address in passed parameter 
248  */
249 int skip_spaces(char **msg)
250 {
251    char *p = *msg;
252    if (!p) {
253       return 0;
254    }
255    while (*p && *p == ' ') {
256       p++;
257    }
258    *msg = p;
259    return *p ? 1 : 0;
260 }
261
262 /*
263  * Skip nonspaces
264  *  Returns: 0 on failure (EOF)             
265  *           1 on success
266  *           new address in passed parameter 
267  */
268 int skip_nonspaces(char **msg)
269 {
270    char *p = *msg;
271
272    if (!p) {
273       return 0;
274    }
275    while (*p && *p != ' ') {
276       p++;
277    }
278    *msg = p;
279    return *p ? 1 : 0;
280 }
281
282 /* folded search for string - case insensitive */
283 int
284 fstrsch(char *a, char *b)   /* folded case search */
285 {
286    register char *s1,*s2;
287    register char c1, c2;
288
289    s1=a;
290    s2=b;
291    while (*s1) {                      /* do it the fast way */
292       if ((*s1++ | 0x20) != (*s2++ | 0x20))
293          return 0;                    /* failed */
294    }
295    while (*a) {                       /* do it over the correct slow way */
296       if (ISUPPER(c1 = *a)) {
297          c1 = tolower((int)c1);
298       }
299       if (ISUPPER(c2 = *b)) {
300          c2 = tolower((int)c2);
301       }
302       if (c1 != c2) {
303          return 0;
304       }
305       a++;
306       b++;
307    }
308    return 1;
309 }
310
311
312 char *encode_time(time_t time, char *buf)
313 {
314    struct tm tm;
315    int n = 0;
316
317    if (localtime_r(&time, &tm)) {
318       n = sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d",
319                    tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
320                    tm.tm_hour, tm.tm_min, tm.tm_sec);
321    }
322    return buf+n;
323 }
324
325 /*
326  * Concatenate a string (str) onto a pool memory buffer pm
327  */
328 void pm_strcat(POOLMEM **pm, char *str)
329 {
330    int pmlen = strlen(*pm);
331    int len = strlen(str) + 1;
332
333    *pm = check_pool_memory_size(*pm, pmlen + len);
334    memcpy(*pm+pmlen, str, len);
335 }
336
337
338 /*
339  * Copy a string (str) into a pool memory buffer pm
340  */
341 void pm_strcpy(POOLMEM **pm, char *str)
342 {
343    int len = strlen(str) + 1;
344
345    *pm = check_pool_memory_size(*pm, len);
346    memcpy(*pm, str, len);
347 }
348
349
350 /*
351  * Convert a JobStatus code into a human readable form
352  */
353 void jobstatus_to_ascii(int JobStatus, char *msg, int maxlen)
354 {
355    char *termstat, jstat[2];
356
357    switch (JobStatus) {
358       case JS_Terminated:
359          termstat = _("OK");
360          break;
361      case JS_FatalError:
362      case JS_ErrorTerminated:
363          termstat = _("Error");
364          break;
365      case JS_Error:
366          termstat = _("Non-fatal error");
367          break;
368      case JS_Cancelled:
369          termstat = _("Cancelled");
370          break;
371      case JS_Differences:
372          termstat = _("Verify differences");
373          break;
374      default:
375          jstat[0] = last_job.JobStatus;
376          jstat[1] = 0;
377          termstat = jstat;
378          break;
379    }
380    strncpy(msg, termstat, maxlen);
381    msg[maxlen-1] = 0;
382 }
383
384 /*
385  * Convert Job Termination Status into a string
386  */
387 char *job_status_to_str(int stat) 
388 {
389    char *str;
390
391    switch (stat) {
392    case JS_Terminated:
393       str = _("OK");
394       break;
395    case JS_ErrorTerminated:
396    case JS_Error:
397       str = _("Error");
398       break;
399    case JS_FatalError:
400       str = _("Fatal Error");
401       break;
402    case JS_Cancelled:
403       str = _("Cancelled");
404       break;
405    case JS_Differences:
406       str = _("Differences");
407       break;
408    default:
409       str = _("Unknown term code");
410       break;
411    }
412    return str;
413 }
414
415
416 /*
417  * Convert Job Type into a string
418  */
419 char *job_type_to_str(int type) 
420 {
421    char *str;
422
423    switch (type) {
424    case JT_BACKUP:
425       str = _("Backup");
426       break;
427    case JT_VERIFY:
428       str = _("Verify");
429       break;
430    case JT_RESTORE:
431       str = _("Restore");
432       break;
433    case JT_ADMIN:
434       str = _("Admin");
435    default:
436       str = _("Unknown Type");
437       break;
438    }
439    return str;
440 }
441
442 /*
443  * Convert Job Level into a string
444  */
445 char *job_level_to_str(int level) 
446 {
447    char *str;
448
449    switch (level) {
450    case L_FULL:
451       str = _("Full");
452       break;
453    case L_INCREMENTAL:
454       str = _("Incremental");
455       break;
456    case L_DIFFERENTIAL:
457       str = _("Differential");
458       break;
459    case L_LEVEL:
460       str = _("Level");
461       break;
462    case L_SINCE:
463       str = _("Since");
464       break;
465    case L_VERIFY_CATALOG:
466       str = _("Verify Catalog");
467       break;
468    case L_VERIFY_INIT:
469       str = _("Verify Init Catalog");
470       break;
471    case L_VERIFY_VOLUME_TO_CATALOG:
472       str = _("Verify Volume to Catalog");
473       break;
474    case L_VERIFY_DATA:
475       str = _("Verify Data");
476       break;
477    default:
478       str = _("Unknown Job Level");
479       break;
480    }
481    return str;
482 }
483
484
485 /***********************************************************************
486  * Encode the mode bits into a 10 character string like LS does
487  ***********************************************************************/
488
489 char *encode_mode(mode_t mode, char *buf)
490 {
491   char *cp = buf;  
492
493   *cp++ = S_ISDIR(mode) ? 'd' : S_ISBLK(mode) ? 'b' : S_ISCHR(mode) ? 'c' :
494           S_ISLNK(mode) ? 'l' : '-';
495   *cp++ = mode & S_IRUSR ? 'r' : '-';
496   *cp++ = mode & S_IWUSR ? 'w' : '-';
497   *cp++ = (mode & S_ISUID
498                ? (mode & S_IXUSR ? 's' : 'S')
499                : (mode & S_IXUSR ? 'x' : '-'));
500   *cp++ = mode & S_IRGRP ? 'r' : '-';
501   *cp++ = mode & S_IWGRP ? 'w' : '-';
502   *cp++ = (mode & S_ISGID
503                ? (mode & S_IXGRP ? 's' : 'S')
504                : (mode & S_IXGRP ? 'x' : '-'));
505   *cp++ = mode & S_IROTH ? 'r' : '-';
506   *cp++ = mode & S_IWOTH ? 'w' : '-';
507   *cp++ = (mode & S_ISVTX
508                ? (mode & S_IXOTH ? 't' : 'T')
509                : (mode & S_IXOTH ? 'x' : '-'));
510   *cp = '\0';
511   return cp;
512 }
513
514
515 int do_shell_expansion(char *name)
516 {
517 /*  ****FIXME***** this should work for Win32 too */
518 #define UNIX
519 #ifdef UNIX
520 #ifndef PATH_MAX
521 #define PATH_MAX 512
522 #endif
523
524    int pid, wpid, stat;
525    int waitstatus;
526    char *shellcmd;
527    void (*istat)(int), (*qstat)(int);
528    int i;
529    char echout[PATH_MAX + 256];
530    int pfd[2];
531    static char meta[] = "~\\$[]*?`'<>\"";
532    int found = FALSE;
533    int len;
534
535    /* Check if any meta characters are present */
536    len = strlen(meta);
537    for (i = 0; i < len; i++) {
538       if (strchr(name, meta[i])) {
539          found = TRUE;
540          break;
541       }
542    }
543    stat = 0;
544    if (found) {
545 #ifdef nt
546        /* If the filename appears to be a DOS filename,
547           convert all backward slashes \ to Unix path
548           separators / and insert a \ infront of spaces. */
549        len = strlen(name);
550        if (len >= 3 && name[1] == ':' && name[2] == '\\') {
551           for (i=2; i<len; i++)
552              if (name[i] == '\\')
553                 name[i] = '/';
554        }
555 #else
556        /* Pass string off to the shell for interpretation */
557        if (pipe(pfd) == -1)
558           return 0;
559        switch(pid = fork()) {
560        case -1:
561           break;
562
563        case 0:                            /* child */
564           /* look for shell */
565           if ((shellcmd = getenv("SHELL")) == NULL)
566              shellcmd = "/bin/sh";
567           close(1); dup(pfd[1]);          /* attach pipes to stdin and stdout */
568           close(2); dup(pfd[1]);
569           for (i = 3; i < 32; i++)        /* close everything else */
570              close(i);
571           strcpy(echout, "echo ");        /* form echo command */
572           strcat(echout, name);
573           execl(shellcmd, shellcmd, "-c", echout, NULL); /* give to shell */
574           exit(127);                      /* shouldn't get here */
575
576        default:                           /* parent */
577           /* read output from child */
578           i = read(pfd[0], echout, sizeof echout);
579           echout[--i] = 0;                /* set end of string */
580           /* look for first word or first line. */
581           while (--i >= 0) {
582              if (echout[i] == ' ' || echout[i] == '\n')
583                 echout[i] = 0;            /* keep only first one */
584           }
585           istat = signal(SIGINT, SIG_IGN);
586           qstat = signal(SIGQUIT, SIG_IGN);
587           /* wait for child to exit */
588           while ((wpid = wait(&waitstatus)) != pid && wpid != -1)
589              { ; }
590           signal(SIGINT, istat);
591           signal(SIGQUIT, qstat);
592           strcpy(name, echout);
593           stat = 1;
594           break;
595        }
596        close(pfd[0]);                     /* close pipe */
597        close(pfd[1]);
598 #endif /* nt */
599    }
600    return stat;
601
602 #endif /* UNIX */
603
604 #if  MSC | MSDOS | __WATCOMC__
605
606    char prefix[100], *env, *getenv();
607
608    /* Home directory reference? */
609    if (*name == '~' && (env=getenv("HOME"))) {
610       strcpy(prefix, env);            /* copy HOME directory name */
611       name++;                         /* skip over ~ in name */
612       strcat(prefix, name);
613       name--;                         /* get back to beginning */
614       strcpy(name, prefix);           /* move back into name */
615    }
616    return 1;
617 #endif
618
619 }
620
621 #define MAX_ARGV 100
622 static void build_argc_argv(char *cmd, int *bargc, char *bargv[], int max_arg);
623
624 /*
625  * Run an external program. Optionally wait a specified number
626  *   of seconds. Program killed if wait exceeded. Optionally
627  *   return the output from the program (normally a single line).
628  */
629 int run_program(char *prog, int wait, POOLMEM *results)
630 {
631    int stat = ETIME;
632    int chldstatus = 0;
633    pid_t pid1, pid2 = 0;
634    int pfd[2];
635    char *bargv[MAX_ARGV];
636    int bargc;
637
638    
639    build_argc_argv(prog, &bargc, bargv, MAX_ARGV);
640 #ifdef xxxxxxxxxx
641    printf("argc=%d\n", bargc);
642    int i;
643    for (i=0; i<bargc; i++) {
644       printf("argc=%d argv=%s\n", i, bargv[i]);
645    }
646 #endif
647
648    if (results && pipe(pfd) == -1) {
649       return errno;
650    }
651    /* Start worker process */
652    switch (pid1 = fork()) {
653    case -1:
654       break;
655
656    case 0:                            /* child */
657 //    printf("execl of %s\n", prog);
658       if (results) {
659          close(1); dup(pfd[1]);       /* attach pipes to stdin and stdout */
660          close(2); dup(pfd[1]);
661       }
662       execvp(bargv[0], bargv);
663       exit(errno);                   /* shouldn't get here */
664
665    default:                           /* parent */
666       /* start timer process */
667       if (wait > 0) {
668          switch (pid2=fork()) {
669          case -1:
670             break;
671          case 0:                         /* child 2 */
672             /* Time the worker process */  
673             sleep(wait);
674             if (kill(pid1, SIGTERM) == 0) { /* time expired kill it */
675                exit(0);
676             }
677             sleep(3);
678             kill(pid1, SIGKILL);
679             exit(0);
680          default:                        /* parent */
681             break;
682          }
683       }
684
685       /* Parent continues here */
686       int i;
687       if (results) {
688          i = read(pfd[0], results, sizeof_pool_memory(results) - 1);
689          if (--i < 0) {
690             i = 0;
691          }
692          results[i] = 0;                /* set end of string */
693       }
694       /* wait for worker child to exit */
695       for ( ;; ) {
696          pid_t wpid;
697          wpid = waitpid(pid1, &chldstatus, 0);         
698          if (wpid == pid1 || (errno != EINTR)) {
699             break;
700          }
701       }
702       if (WIFEXITED(chldstatus))
703          stat = WEXITSTATUS(chldstatus);
704
705       if (wait > 0) {
706          kill(pid2, SIGKILL);           /* kill off timer process */
707          waitpid(pid2, &chldstatus, 0); /* reap timer process */
708       }
709       if (results) { 
710          close(pfd[0]);              /* close pipe */
711          close(pfd[1]);
712       }
713       break;
714    }
715    return stat;
716 }
717
718 /*
719  * Build argc and argv from a string
720  */
721 static void build_argc_argv(char *cmd, int *bargc, char *bargv[], int max_argv)
722 {
723    int i, quote;
724    char *p, *q;
725    int argc = 0;
726
727    argc = 0;
728    for (i=0; i<max_argv; i++)
729       bargv[i] = NULL;
730
731    p = cmd;
732    quote = 0;
733    while  (*p && (*p == ' ' || *p == '\t'))
734       p++;
735    if (*p == '\"') {
736       quote = 1;
737       p++;
738    }
739    if (*p) {
740       while (*p && argc < MAX_ARGV) {
741          q = p;
742          if (quote) {
743             while (*q && *q != '\"')
744             q++;
745             quote = 0;
746          } else {
747             while (*q && *q != ' ')
748             q++;
749          }
750          if (*q)
751             *(q++) = '\0';
752          bargv[argc++] = p;
753          p = q;
754          while (*p && (*p == ' ' || *p == '\t'))
755             p++;
756          if (*p == '\"') {
757             quote = 1;
758             p++;
759          }
760       }
761    }
762    *bargc = argc;
763 }
764
765 /*  MAKESESSIONKEY  --  Generate session key with optional start
766                         key.  If mode is TRUE, the key will be
767                         translated to a string, otherwise it is
768                         returned as 16 binary bytes.
769
770     from SpeakFreely by John Walker */
771
772 void makeSessionKey(char *key, char *seed, int mode)
773 {
774      int j, k;
775      struct MD5Context md5c;
776      unsigned char md5key[16], md5key1[16];
777      char s[1024];
778
779      s[0] = 0;
780      if (seed != NULL) {
781         strcat(s, seed);
782      }
783
784      /* The following creates a seed for the session key generator
785         based on a collection of volatile and environment-specific
786         information unlikely to be vulnerable (as a whole) to an
787         exhaustive search attack.  If one of these items isn't
788         available on your machine, replace it with something
789         equivalent or, if you like, just delete it. */
790
791      sprintf(s + strlen(s), "%lu", (unsigned long) getpid());
792      sprintf(s + strlen(s), "%lu", (unsigned long) getppid());
793      getcwd(s + strlen(s), 256);
794      sprintf(s + strlen(s), "%lu", (unsigned long) clock());
795      sprintf(s + strlen(s), "%lu", (unsigned long) time(NULL));
796 #ifdef Solaris
797      sysinfo(SI_HW_SERIAL,s + strlen(s), 12);
798 #endif
799 #ifdef HAVE_GETHOSTID
800      sprintf(s + strlen(s), "%lu", (unsigned long) gethostid());
801 #endif
802 #ifdef HAVE_GETDOMAINNAME
803      getdomainname(s + strlen(s), 256);
804 #endif
805      gethostname(s + strlen(s), 256);
806      sprintf(s + strlen(s), "%u", (unsigned)getuid());
807      sprintf(s + strlen(s), "%u", (unsigned)getgid());
808      MD5Init(&md5c);
809      MD5Update(&md5c, (unsigned char *)s, strlen(s));
810      MD5Final(md5key, &md5c);
811      sprintf(s + strlen(s), "%lu", (unsigned long) ((time(NULL) + 65121) ^ 0x375F));
812      MD5Init(&md5c);
813      MD5Update(&md5c, (unsigned char *)s, strlen(s));
814      MD5Final(md5key1, &md5c);
815 #define nextrand    (md5key[j] ^ md5key1[j])
816      if (mode) {
817         for (j = k = 0; j < 16; j++) {
818             unsigned char rb = nextrand;
819
820 #define Rad16(x) ((x) + 'A')
821             key[k++] = Rad16((rb >> 4) & 0xF);
822             key[k++] = Rad16(rb & 0xF);
823 #undef Rad16
824             if (j & 1) {
825                  key[k++] = '-';
826             }
827         }
828         key[--k] = 0;
829      } else {
830         for (j = 0; j < 16; j++) {
831             key[j] = nextrand;
832         }
833      }
834 }
835 #undef nextrand