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