]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/util.c
LabelFormat
[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 /* Return true of buffer has all zero bytes */
39 int is_buf_zero(char *buf, int len)
40 {
41    uint64_t *ip = (uint64_t *)buf;
42    char *p;
43    int i, len64, done, rem;
44
45    /* Optimize by checking uint64_t for zero */
46    len64 = len >> sizeof(uint64_t);
47    for (i=0; i < len64; i++) {
48       if (ip[i] != 0) {
49          return 0;
50       }
51    }
52    done = len64 << sizeof(uint64_t);  /* bytes already checked */
53    p = buf + done;
54    rem = len - done;
55    for (i = 0; i < rem; i++) {
56       if (p[i] != 0) {
57          return 0;
58       }
59    }
60    return 1;
61 }
62
63
64 /* Convert a string in place to lower case */
65 void lcase(char *str)
66 {
67    while (*str) {
68       if (B_ISUPPER(*str))
69          *str = tolower((int)(*str));
70        str++;
71    }
72 }
73
74 /* Convert spaces to non-space character. 
75  * This makes scanf of fields containing spaces easier.
76  */
77 void
78 bash_spaces(char *str)
79 {
80    while (*str) {
81       if (*str == ' ')
82          *str = 0x1;
83       str++;
84    }
85 }
86
87 /* Convert non-space characters (0x1) back into spaces */
88 void
89 unbash_spaces(char *str)
90 {
91    while (*str) {
92      if (*str == 0x1)
93         *str = ' ';
94      str++;
95    }
96 }
97
98
99 char *encode_time(time_t time, char *buf)
100 {
101    struct tm tm;
102    int n = 0;
103
104    if (localtime_r(&time, &tm)) {
105       n = sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d",
106                    tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
107                    tm.tm_hour, tm.tm_min, tm.tm_sec);
108    }
109    return buf+n;
110 }
111
112 /*
113  * Concatenate a string (str) onto a pool memory buffer pm
114  */
115 void pm_strcat(POOLMEM **pm, char *str)
116 {
117    int pmlen = strlen(*pm);
118    int len = strlen(str) + 1;
119
120    *pm = check_pool_memory_size(*pm, pmlen + len);
121    memcpy(*pm+pmlen, str, len);
122 }
123
124
125 /*
126  * Copy a string (str) into a pool memory buffer pm
127  */
128 void pm_strcpy(POOLMEM **pm, char *str)
129 {
130    int len = strlen(str) + 1;
131
132    *pm = check_pool_memory_size(*pm, len);
133    memcpy(*pm, str, len);
134 }
135
136
137 /*
138  * Convert a JobStatus code into a human readable form
139  */
140 void jobstatus_to_ascii(int JobStatus, char *msg, int maxlen)
141 {
142    char *termstat, jstat[2];
143
144    switch (JobStatus) {
145       case JS_Terminated:
146          termstat = _("OK");
147          break;
148      case JS_FatalError:
149      case JS_ErrorTerminated:
150          termstat = _("Error");
151          break;
152      case JS_Error:
153          termstat = _("Non-fatal error");
154          break;
155      case JS_Canceled:
156          termstat = _("Canceled");
157          break;
158      case JS_Differences:
159          termstat = _("Verify differences");
160          break;
161      default:
162          jstat[0] = last_job.JobStatus;
163          jstat[1] = 0;
164          termstat = jstat;
165          break;
166    }
167    bstrncpy(msg, termstat, maxlen);
168 }
169
170 /*
171  * Convert Job Termination Status into a string
172  */
173 char *job_status_to_str(int stat) 
174 {
175    char *str;
176
177    switch (stat) {
178    case JS_Terminated:
179       str = _("OK");
180       break;
181    case JS_ErrorTerminated:
182    case JS_Error:
183       str = _("Error");
184       break;
185    case JS_FatalError:
186       str = _("Fatal Error");
187       break;
188    case JS_Canceled:
189       str = _("Canceled");
190       break;
191    case JS_Differences:
192       str = _("Differences");
193       break;
194    default:
195       str = _("Unknown term code");
196       break;
197    }
198    return str;
199 }
200
201
202 /*
203  * Convert Job Type into a string
204  */
205 char *job_type_to_str(int type) 
206 {
207    char *str;
208
209    switch (type) {
210    case JT_BACKUP:
211       str = _("Backup");
212       break;
213    case JT_VERIFY:
214       str = _("Verify");
215       break;
216    case JT_RESTORE:
217       str = _("Restore");
218       break;
219    case JT_ADMIN:
220       str = _("Admin");
221       break;
222    default:
223       str = _("Unknown Type");
224       break;
225    }
226    return str;
227 }
228
229 /*
230  * Convert Job Level into a string
231  */
232 char *job_level_to_str(int level) 
233 {
234    char *str;
235
236    switch (level) {
237    case L_BASE:
238       str = _("Base");
239    case L_FULL:
240       str = _("Full");
241       break;
242    case L_INCREMENTAL:
243       str = _("Incremental");
244       break;
245    case L_DIFFERENTIAL:
246       str = _("Differential");
247       break;
248    case L_SINCE:
249       str = _("Since");
250       break;
251    case L_VERIFY_CATALOG:
252       str = _("Verify Catalog");
253       break;
254    case L_VERIFY_INIT:
255       str = _("Verify Init Catalog");
256       break;
257    case L_VERIFY_VOLUME_TO_CATALOG:
258       str = _("Verify Volume to Catalog");
259       break;
260    case L_VERIFY_DATA:
261       str = _("Verify Data");
262       break;
263    default:
264       str = _("Unknown Job Level");
265       break;
266    }
267    return str;
268 }
269
270
271 /***********************************************************************
272  * Encode the mode bits into a 10 character string like LS does
273  ***********************************************************************/
274
275 char *encode_mode(mode_t mode, char *buf)
276 {
277   char *cp = buf;  
278
279   *cp++ = S_ISDIR(mode) ? 'd' : S_ISBLK(mode)  ? 'b' : S_ISCHR(mode)  ? 'c' :
280           S_ISLNK(mode) ? 'l' : S_ISFIFO(mode) ? 'f' : S_ISSOCK(mode) ? 's' : '-';
281   *cp++ = mode & S_IRUSR ? 'r' : '-';
282   *cp++ = mode & S_IWUSR ? 'w' : '-';
283   *cp++ = (mode & S_ISUID
284                ? (mode & S_IXUSR ? 's' : 'S')
285                : (mode & S_IXUSR ? 'x' : '-'));
286   *cp++ = mode & S_IRGRP ? 'r' : '-';
287   *cp++ = mode & S_IWGRP ? 'w' : '-';
288   *cp++ = (mode & S_ISGID
289                ? (mode & S_IXGRP ? 's' : 'S')
290                : (mode & S_IXGRP ? 'x' : '-'));
291   *cp++ = mode & S_IROTH ? 'r' : '-';
292   *cp++ = mode & S_IWOTH ? 'w' : '-';
293   *cp++ = (mode & S_ISVTX
294                ? (mode & S_IXOTH ? 't' : 'T')
295                : (mode & S_IXOTH ? 'x' : '-'));
296   *cp = '\0';
297   return cp;
298 }
299
300
301 int do_shell_expansion(char *name, int name_len)
302 {
303 /*  ****FIXME***** this should work for Win32 too */
304 #define UNIX
305 #ifdef UNIX
306 #ifndef PATH_MAX
307 #define PATH_MAX 512
308 #endif
309
310    int pid, wpid, stat;
311    int waitstatus;
312    char *shellcmd;
313    int i;
314    char echout[PATH_MAX + 256];
315    int pfd[2];
316    static char meta[] = "~\\$[]*?`'<>\"";
317    int found = FALSE;
318    int len;
319
320    /* Check if any meta characters are present */
321    len = strlen(meta);
322    for (i = 0; i < len; i++) {
323       if (strchr(name, meta[i])) {
324          found = TRUE;
325          break;
326       }
327    }
328    stat = 0;
329    if (found) {
330 #ifdef nt
331        /* If the filename appears to be a DOS filename,
332           convert all backward slashes \ to Unix path
333           separators / and insert a \ infront of spaces. */
334        len = strlen(name);
335        if (len >= 3 && name[1] == ':' && name[2] == '\\') {
336           for (i=2; i<len; i++)
337              if (name[i] == '\\')
338                 name[i] = '/';
339        }
340 #else
341        /* Pass string off to the shell for interpretation */
342        if (pipe(pfd) == -1)
343           return 0;
344        switch(pid = fork()) {
345        case -1:
346           break;
347
348        case 0:                            /* child */
349           /* look for shell */
350           if ((shellcmd = getenv("SHELL")) == NULL) {
351              shellcmd = "/bin/sh";
352           }
353           close(pfd[0]);                  /* close stdin */
354           dup2(pfd[1], 1);                /* attach to stdout */
355           dup2(pfd[1], 2);                /* and stderr */
356           strcpy(echout, "echo ");        /* form echo command */
357           bstrncat(echout, name, sizeof(echout));
358           execl(shellcmd, shellcmd, "-c", echout, NULL); /* give to shell */
359           exit(127);                      /* shouldn't get here */
360
361        default:                           /* parent */
362           /* read output from child */
363           echout[0] = 0;
364           do {
365              i = read(pfd[0], echout, sizeof echout);
366           } while (i == -1 && errno == EINTR); 
367
368           if (i > 0) {
369              echout[--i] = 0;                /* set end of string */
370              /* look for first line. */
371              while (--i >= 0) {
372                 if (echout[i] == '\n') {
373                    echout[i] = 0;            /* keep only first one */
374                 }
375              }
376           }
377           /* wait for child to exit */
378           while ((wpid = wait(&waitstatus)) != pid && wpid != -1)
379              { ; }
380           strip_trailing_junk(echout);
381           if (strlen(echout) > 0) {
382              bstrncpy(name, echout, name_len);
383           }
384           stat = 1;
385           break;
386        }
387        close(pfd[0]);                     /* close pipe */
388        close(pfd[1]);
389 #endif /* nt */
390    }
391    return stat;
392
393 #endif /* UNIX */
394
395 #if  MSC | MSDOS | __WATCOMC__
396
397    char prefix[100], *env, *getenv();
398
399    /* Home directory reference? */
400    if (*name == '~' && (env=getenv("HOME"))) {
401       strcpy(prefix, env);            /* copy HOME directory name */
402       name++;                         /* skip over ~ in name */
403       strcat(prefix, name);
404       name--;                         /* get back to beginning */
405       strcpy(name, prefix);           /* move back into name */
406    }
407    return 1;
408 #endif
409
410 }
411
412
413 /*  MAKESESSIONKEY  --  Generate session key with optional start
414                         key.  If mode is TRUE, the key will be
415                         translated to a string, otherwise it is
416                         returned as 16 binary bytes.
417
418     from SpeakFreely by John Walker */
419
420 void make_session_key(char *key, char *seed, int mode)
421 {
422      int j, k;
423      struct MD5Context md5c;
424      unsigned char md5key[16], md5key1[16];
425      char s[1024];
426
427      s[0] = 0;
428      if (seed != NULL) {
429         strcat(s, seed);
430      }
431
432      /* The following creates a seed for the session key generator
433         based on a collection of volatile and environment-specific
434         information unlikely to be vulnerable (as a whole) to an
435         exhaustive search attack.  If one of these items isn't
436         available on your machine, replace it with something
437         equivalent or, if you like, just delete it. */
438
439      sprintf(s + strlen(s), "%lu", (unsigned long) getpid());
440      sprintf(s + strlen(s), "%lu", (unsigned long) getppid());
441      getcwd(s + strlen(s), 256);
442      sprintf(s + strlen(s), "%lu", (unsigned long) clock());
443      sprintf(s + strlen(s), "%lu", (unsigned long) time(NULL));
444 #ifdef Solaris
445      sysinfo(SI_HW_SERIAL,s + strlen(s), 12);
446 #endif
447 #ifdef HAVE_GETHOSTID
448      sprintf(s + strlen(s), "%lu", (unsigned long) gethostid());
449 #endif
450 #ifdef HAVE_GETDOMAINNAME
451      getdomainname(s + strlen(s), 256);
452 #endif
453      gethostname(s + strlen(s), 256);
454      sprintf(s + strlen(s), "%u", (unsigned)getuid());
455      sprintf(s + strlen(s), "%u", (unsigned)getgid());
456      MD5Init(&md5c);
457      MD5Update(&md5c, (unsigned char *)s, strlen(s));
458      MD5Final(md5key, &md5c);
459      sprintf(s + strlen(s), "%lu", (unsigned long) ((time(NULL) + 65121) ^ 0x375F));
460      MD5Init(&md5c);
461      MD5Update(&md5c, (unsigned char *)s, strlen(s));
462      MD5Final(md5key1, &md5c);
463 #define nextrand    (md5key[j] ^ md5key1[j])
464      if (mode) {
465         for (j = k = 0; j < 16; j++) {
466             unsigned char rb = nextrand;
467
468 #define Rad16(x) ((x) + 'A')
469             key[k++] = Rad16((rb >> 4) & 0xF);
470             key[k++] = Rad16(rb & 0xF);
471 #undef Rad16
472             if (j & 1) {
473                  key[k++] = '-';
474             }
475         }
476         key[--k] = 0;
477      } else {
478         for (j = 0; j < 16; j++) {
479             key[j] = nextrand;
480         }
481      }
482 }
483 #undef nextrand
484
485
486
487 /*
488  * Edit job codes into main command line
489  *  %% = %
490  *  %j = Job name
491  *  %t = Job type (Backup, ...)
492  *  %e = Job Exit code
493  *  %i = JobId
494  *  %l = job level
495  *  %c = Client's name
496  *  %r = Recipients
497  *  %d = Director's name
498  *
499  *  omsg = edited output message
500  *  imsg = input string containing edit codes (%x)
501  *  to = recepients list 
502  *
503  */
504 POOLMEM *edit_job_codes(JCR *jcr, char *omsg, char *imsg, char *to)   
505 {
506    char *p, *str;
507    char add[20];
508
509    *omsg = 0;
510    Dmsg1(200, "edit_job_codes: %s\n", imsg);
511    for (p=imsg; *p; p++) {
512       if (*p == '%') {
513          switch (*++p) {
514          case '%':
515             str = "%";
516             break;
517          case 'c':
518             str = jcr->client_name;
519             if (!str) {
520                str = "";
521             }
522             break;
523          case 'd':
524             str = my_name;            /* Director's name */
525             break;
526          case 'e':
527             str = job_status_to_str(jcr->JobStatus); 
528             break;
529          case 'i':
530             bsnprintf(add, sizeof(add), "%d", jcr->JobId);
531             str = add;
532             break;
533          case 'j':                    /* Job name */
534             str = jcr->Job;
535             break;
536          case 'l':
537             str = job_level_to_str(jcr->JobLevel);
538             break;
539          case 'r':
540             str = to;
541             break;
542          case 't':
543             str = job_type_to_str(jcr->JobType);
544             break;
545          default:
546             add[0] = '%';
547             add[1] = *p;
548             add[2] = 0;
549             str = add;
550             break;
551          }
552       } else {
553          add[0] = *p;
554          add[1] = 0;
555          str = add;
556       }
557       Dmsg1(1200, "add_str %s\n", str);
558       pm_strcat(&omsg, str);
559       Dmsg1(1200, "omsg=%s\n", omsg);
560    }
561    return omsg;
562 }
563
564 void set_working_directory(char *wd)
565 {
566    struct stat stat_buf; 
567
568    if (wd == NULL) {
569       Emsg0(M_ERROR_TERM, 0, _("Working directory not defined. Cannot continue.\n"));
570    }
571    if (stat(wd, &stat_buf) != 0) {
572       Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" not found. Cannot continue.\n"),
573          wd);
574    }
575    if (!S_ISDIR(stat_buf.st_mode)) {
576       Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" is not a directory. Cannot continue.\n"),
577          wd);
578    }
579    working_directory = wd;            /* set global */
580 }