]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/util.c
19July06
[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    Copyright (C) 2000-2006 Kern Sibbald
10
11    This program is free software; you can redistribute it and/or
12    modify it under the terms of the GNU General Public License
13    version 2 as amended with additional clauses defined in the
14    file LICENSE in the main source directory.
15
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
19    the file LICENSE for additional details.
20
21  */
22
23 #include "bacula.h"
24 #include "jcr.h"
25 #include "findlib/find.h"
26
27 /*
28  * Various Bacula Utility subroutines
29  *
30  */
31
32 /* Return true of buffer has all zero bytes */
33 int is_buf_zero(char *buf, int len)
34 {
35    uint64_t *ip;
36    char *p;
37    int i, len64, done, rem;
38
39    if (buf[0] != 0) {
40       return 0;
41    }
42    ip = (uint64_t *)buf;
43    /* Optimize by checking uint64_t for zero */
44    len64 = len / sizeof(uint64_t);
45    for (i=0; i < len64; i++) {
46       if (ip[i] != 0) {
47          return 0;
48       }
49    }
50    done = len64 * sizeof(uint64_t);  /* bytes already checked */
51    p = buf + done;
52    rem = len - done;
53    for (i = 0; i < rem; i++) {
54       if (p[i] != 0) {
55          return 0;
56       }
57    }
58    return 1;
59 }
60
61
62 /* Convert a string in place to lower case */
63 void lcase(char *str)
64 {
65    while (*str) {
66       if (B_ISUPPER(*str))
67          *str = tolower((int)(*str));
68        str++;
69    }
70 }
71
72 /* Convert spaces to non-space character.
73  * This makes scanf of fields containing spaces easier.
74  */
75 void
76 bash_spaces(char *str)
77 {
78    while (*str) {
79       if (*str == ' ')
80          *str = 0x1;
81       str++;
82    }
83 }
84
85 /* Convert spaces to non-space character.
86  * This makes scanf of fields containing spaces easier.
87  */
88 void
89 bash_spaces(POOL_MEM &pm)
90 {
91    char *str = pm.c_str();
92    while (*str) {
93       if (*str == ' ')
94          *str = 0x1;
95       str++;
96    }
97 }
98
99
100 /* Convert non-space characters (0x1) back into spaces */
101 void
102 unbash_spaces(char *str)
103 {
104    while (*str) {
105      if (*str == 0x1)
106         *str = ' ';
107      str++;
108    }
109 }
110
111 /* Convert non-space characters (0x1) back into spaces */
112 void
113 unbash_spaces(POOL_MEM &pm)
114 {
115    char *str = pm.c_str();
116    while (*str) {
117      if (*str == 0x1)
118         *str = ' ';
119      str++;
120    }
121 }
122
123 char *encode_time(time_t time, char *buf)
124 {
125    struct tm tm;
126    int n = 0;
127
128 #if defined(HAVE_WIN32)
129    /*
130     * Avoid a seg fault in Microsoft's CRT localtime_r(),
131     *  which incorrectly references a NULL returned from gmtime() if
132     *  time is negative before or after the timezone adjustment.
133     */
134    struct tm *gtm;
135
136    if ((gtm = gmtime(&time)) == NULL) {
137       return buf;
138    }
139
140    if (gtm->tm_year == 1970 && gtm->tm_mon == 1 && gtm->tm_mday < 3) {
141       return buf;
142    }
143 #endif
144
145    if (localtime_r(&time, &tm)) {
146       n = sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d",
147                    tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
148                    tm.tm_hour, tm.tm_min, tm.tm_sec);
149    }
150    return buf+n;
151 }
152
153
154
155 /*
156  * Convert a JobStatus code into a human readable form
157  */
158 void jobstatus_to_ascii(int JobStatus, char *msg, int maxlen)
159 {
160    const char *jobstat;
161    char buf[100];
162
163    switch (JobStatus) {
164    case JS_Created:
165       jobstat = _("Created");
166       break;
167    case JS_Running:
168       jobstat = _("Running");
169       break;
170    case JS_Blocked:
171       jobstat = _("Blocked");
172       break;
173    case JS_Terminated:
174       jobstat = _("OK");
175       break;
176    case JS_FatalError:
177    case JS_ErrorTerminated:
178       jobstat = _("Error");
179       break;
180    case JS_Error:
181       jobstat = _("Non-fatal error");
182       break;
183    case JS_Canceled:
184       jobstat = _("Canceled");
185       break;
186    case JS_Differences:
187       jobstat = _("Verify differences");
188       break;
189    case JS_WaitFD:
190       jobstat = _("Waiting on FD");
191       break;
192    case JS_WaitSD:
193       jobstat = _("Wait on SD");
194       break;
195    case JS_WaitMedia:
196       jobstat = _("Wait for new Volume");
197       break;
198    case JS_WaitMount:
199       jobstat = _("Waiting for mount");
200       break;
201    case JS_WaitStoreRes:
202       jobstat = _("Waiting for Storage resource");
203       break;
204    case JS_WaitJobRes:
205       jobstat = _("Waiting for Job resource");
206       break;
207    case JS_WaitClientRes:
208       jobstat = _("Waiting for Client resource");
209       break;
210    case JS_WaitMaxJobs:
211       jobstat = _("Waiting on Max Jobs");
212       break;
213    case JS_WaitStartTime:
214       jobstat = _("Waiting for Start Time");
215       break;
216    case JS_WaitPriority:
217       jobstat = _("Waiting on Priority");
218       break;
219
220    default:
221       if (JobStatus == 0) {
222          buf[0] = 0;
223       } else {
224          bsnprintf(buf, sizeof(buf), _("Unknown Job termination status=%d"), JobStatus);
225       }
226       jobstat = buf;
227       break;
228    }
229    bstrncpy(msg, jobstat, maxlen);
230 }
231
232 /*
233  * Convert Job Termination Status into a string
234  */
235 const char *job_status_to_str(int stat)
236 {
237    const char *str;
238
239    switch (stat) {
240    case JS_Terminated:
241       str = _("OK");
242       break;
243    case JS_ErrorTerminated:
244    case JS_Error:
245       str = _("Error");
246       break;
247    case JS_FatalError:
248       str = _("Fatal Error");
249       break;
250    case JS_Canceled:
251       str = _("Canceled");
252       break;
253    case JS_Differences:
254       str = _("Differences");
255       break;
256    default:
257       str = _("Unknown term code");
258       break;
259    }
260    return str;
261 }
262
263
264 /*
265  * Convert Job Type into a string
266  */
267 const char *job_type_to_str(int type)
268 {
269    const char *str;
270
271    switch (type) {
272    case JT_BACKUP:
273       str = _("Backup");
274       break;
275    case JT_VERIFY:
276       str = _("Verify");
277       break;
278    case JT_RESTORE:
279       str = _("Restore");
280       break;
281    case JT_ADMIN:
282       str = _("Admin");
283       break;
284    case JT_MIGRATE:
285       str = _("Migrate");
286       break;
287    case JT_COPY:
288       str = _("Copy");
289       break;
290    default:
291       str = _("Unknown Type");
292       break;
293    }
294    return str;
295 }
296
297 /*
298  * Convert Job Level into a string
299  */
300 const char *job_level_to_str(int level)
301 {
302    const char *str;
303
304    switch (level) {
305    case L_BASE:
306       str = _("Base");
307    case L_FULL:
308       str = _("Full");
309       break;
310    case L_INCREMENTAL:
311       str = _("Incremental");
312       break;
313    case L_DIFFERENTIAL:
314       str = _("Differential");
315       break;
316    case L_SINCE:
317       str = _("Since");
318       break;
319    case L_VERIFY_CATALOG:
320       str = _("Verify Catalog");
321       break;
322    case L_VERIFY_INIT:
323       str = _("Verify Init Catalog");
324       break;
325    case L_VERIFY_VOLUME_TO_CATALOG:
326       str = _("Verify Volume to Catalog");
327       break;
328    case L_VERIFY_DISK_TO_CATALOG:
329       str = _("Verify Disk to Catalog");
330       break;
331    case L_VERIFY_DATA:
332       str = _("Verify Data");
333       break;
334    case L_NONE:
335       str = " ";
336       break;
337    default:
338       str = _("Unknown Job Level");
339       break;
340    }
341    return str;
342 }
343
344
345 /***********************************************************************
346  * Encode the mode bits into a 10 character string like LS does
347  ***********************************************************************/
348
349 char *encode_mode(mode_t mode, char *buf)
350 {
351   char *cp = buf;
352
353   *cp++ = S_ISDIR(mode) ? 'd' : S_ISBLK(mode)  ? 'b' : S_ISCHR(mode)  ? 'c' :
354           S_ISLNK(mode) ? 'l' : S_ISFIFO(mode) ? 'f' : S_ISSOCK(mode) ? 's' : '-';
355   *cp++ = mode & S_IRUSR ? 'r' : '-';
356   *cp++ = mode & S_IWUSR ? 'w' : '-';
357   *cp++ = (mode & S_ISUID
358                ? (mode & S_IXUSR ? 's' : 'S')
359                : (mode & S_IXUSR ? 'x' : '-'));
360   *cp++ = mode & S_IRGRP ? 'r' : '-';
361   *cp++ = mode & S_IWGRP ? 'w' : '-';
362   *cp++ = (mode & S_ISGID
363                ? (mode & S_IXGRP ? 's' : 'S')
364                : (mode & S_IXGRP ? 'x' : '-'));
365   *cp++ = mode & S_IROTH ? 'r' : '-';
366   *cp++ = mode & S_IWOTH ? 'w' : '-';
367   *cp++ = (mode & S_ISVTX
368                ? (mode & S_IXOTH ? 't' : 'T')
369                : (mode & S_IXOTH ? 'x' : '-'));
370   *cp = '\0';
371   return cp;
372 }
373
374
375 int do_shell_expansion(char *name, int name_len)
376 {
377    static char meta[] = "~\\$[]*?`'<>\"";
378    bool found = false;
379    int len, i, stat;
380    POOLMEM *cmd;
381    BPIPE *bpipe;
382    char line[MAXSTRING];
383    const char *shellcmd;
384
385    /* Check if any meta characters are present */
386    len = strlen(meta);
387    for (i = 0; i < len; i++) {
388       if (strchr(name, meta[i])) {
389          found = true;
390          break;
391       }
392    }
393    if (found) {
394       cmd =  get_pool_memory(PM_FNAME);
395       /* look for shell */
396       if ((shellcmd = getenv("SHELL")) == NULL) {
397          shellcmd = "/bin/sh";
398       }
399       pm_strcpy(&cmd, shellcmd);
400       pm_strcat(&cmd, " -c \"echo ");
401       pm_strcat(&cmd, name);
402       pm_strcat(&cmd, "\"");
403       Dmsg1(400, "Send: %s\n", cmd);
404       if ((bpipe = open_bpipe(cmd, 0, "r"))) {
405          *line = 0;
406          fgets(line, sizeof(line), bpipe->rfd);
407          strip_trailing_junk(line);
408          stat = close_bpipe(bpipe);
409          Dmsg2(400, "stat=%d got: %s\n", stat, line);
410       } else {
411          stat = 1;                    /* error */
412       }
413       free_pool_memory(cmd);
414       if (stat == 0) {
415          bstrncpy(name, line, name_len);
416       }
417    }
418    return 1;
419 }
420
421
422 /*  MAKESESSIONKEY  --  Generate session key with optional start
423                         key.  If mode is TRUE, the key will be
424                         translated to a string, otherwise it is
425                         returned as 16 binary bytes.
426
427     from SpeakFreely by John Walker */
428
429 void make_session_key(char *key, char *seed, int mode)
430 {
431      int j, k;
432      struct MD5Context md5c;
433      unsigned char md5key[16], md5key1[16];
434      char s[1024];
435
436      s[0] = 0;
437      if (seed != NULL) {
438         bstrncat(s, seed, sizeof(s));
439      }
440
441      /* The following creates a seed for the session key generator
442         based on a collection of volatile and environment-specific
443         information unlikely to be vulnerable (as a whole) to an
444         exhaustive search attack.  If one of these items isn't
445         available on your machine, replace it with something
446         equivalent or, if you like, just delete it. */
447
448      sprintf(s + strlen(s), "%lu", (unsigned long)getpid());
449      sprintf(s + strlen(s), "%lu", (unsigned long)getppid());
450      (void)getcwd(s + strlen(s), 256);
451      sprintf(s + strlen(s), "%lu", (unsigned long)clock());
452      sprintf(s + strlen(s), "%lu", (unsigned long)time(NULL));
453 #if defined(Solaris)
454      sysinfo(SI_HW_SERIAL,s + strlen(s), 12);
455 #endif
456 #if defined(HAVE_GETHOSTID)
457      sprintf(s + strlen(s), "%lu", (unsigned long) gethostid());
458 #endif
459      gethostname(s + strlen(s), 256);
460      sprintf(s + strlen(s), "%u", (unsigned)getuid());
461      sprintf(s + strlen(s), "%u", (unsigned)getgid());
462      MD5Init(&md5c);
463      MD5Update(&md5c, (unsigned char *)s, strlen(s));
464      MD5Final(md5key, &md5c);
465      sprintf(s + strlen(s), "%lu", (unsigned long)((time(NULL) + 65121) ^ 0x375F));
466      MD5Init(&md5c);
467      MD5Update(&md5c, (unsigned char *)s, strlen(s));
468      MD5Final(md5key1, &md5c);
469 #define nextrand    (md5key[j] ^ md5key1[j])
470      if (mode) {
471         for (j = k = 0; j < 16; j++) {
472            unsigned char rb = nextrand;
473
474 #define Rad16(x) ((x) + 'A')
475            key[k++] = Rad16((rb >> 4) & 0xF);
476            key[k++] = Rad16(rb & 0xF);
477 #undef Rad16
478            if (j & 1) {
479               key[k++] = '-';
480            }
481         }
482         key[--k] = 0;
483      } else {
484         for (j = 0; j < 16; j++) {
485            key[j] = nextrand;
486         }
487      }
488 }
489 #undef nextrand
490
491
492
493 /*
494  * Edit job codes into main command line
495  *  %% = %
496  *  %c = Client's name
497  *  %d = Director's name
498  *  %e = Job Exit code
499  *  %i = JobId
500  *  %j = Unique Job id
501  *  %l = job level
502  *  %n = Unadorned Job name
503  *  %s = Since time
504  *  %t = Job type (Backup, ...)
505  *  %r = Recipients
506  *  %v = Volume name
507  *
508  *  omsg = edited output message
509  *  imsg = input string containing edit codes (%x)
510  *  to = recepients list
511  *
512  */
513 POOLMEM *edit_job_codes(JCR *jcr, char *omsg, char *imsg, const char *to)
514 {
515    char *p, *q;
516    const char *str;
517    char add[20];
518    char name[MAX_NAME_LENGTH];
519    int i;
520
521    *omsg = 0;
522    Dmsg1(200, "edit_job_codes: %s\n", imsg);
523    for (p=imsg; *p; p++) {
524       if (*p == '%') {
525          switch (*++p) {
526          case '%':
527             str = "%";
528             break;
529          case 'c':
530             if (jcr) {
531                str = jcr->client_name;
532             } else {
533                str = _("*none*");
534             }
535             break;
536          case 'd':
537             str = my_name;            /* Director's name */
538             break;
539          case 'e':
540             if (jcr) {
541                str = job_status_to_str(jcr->JobStatus);
542             } else {
543                str = _("*none*");
544             }
545             break;
546          case 'i':
547             if (jcr) {
548                bsnprintf(add, sizeof(add), "%d", jcr->JobId);
549                str = add;
550             } else {
551                str = _("*none*");
552             }
553             break;
554          case 'j':                    /* Job name */
555             if (jcr) {
556                str = jcr->Job;
557             } else {
558                str = _("*none*");
559             }
560             break;
561          case 'l':
562             if (jcr) {
563                str = job_level_to_str(jcr->JobLevel);
564             } else {
565                str = _("*none*");
566             }
567             break;
568          case 'n':
569              if (jcr) {
570                 bstrncpy(name, jcr->Job, sizeof(name));
571                 /* There are three periods after the Job name */
572                 for (i=0; i<3; i++) {
573                    if ((q=strrchr(name, '.')) != NULL) {
574                        *q = 0;
575                    }
576                 }
577                 str = name;
578              } else {
579                 str = _("*none*");
580              }
581              break;
582          case 'r':
583             str = to;
584             break;
585          case 's':                    /* since time */
586             if (jcr && jcr->stime) {
587                str = jcr->stime;
588             } else {
589                str = _("*none*");
590             }
591             break;
592          case 't':
593             if (jcr) {
594                str = job_type_to_str(jcr->JobType);
595             } else {
596                str = _("*none*");
597             }
598             break;
599          case 'v':
600             if (jcr) {
601                if (jcr->VolumeName && jcr->VolumeName[0]) {
602                   str = jcr->VolumeName;
603                } else {
604                   str = "";
605                }
606             } else {
607                str = _("*none*");
608             }
609             break;
610          default:
611             add[0] = '%';
612             add[1] = *p;
613             add[2] = 0;
614             str = add;
615             break;
616          }
617       } else {
618          add[0] = *p;
619          add[1] = 0;
620          str = add;
621       }
622       Dmsg1(1200, "add_str %s\n", str);
623       pm_strcat(&omsg, str);
624       Dmsg1(1200, "omsg=%s\n", omsg);
625    }
626    return omsg;
627 }
628
629 void set_working_directory(char *wd)
630 {
631    struct stat stat_buf;
632
633    if (wd == NULL) {
634       Emsg0(M_ERROR_TERM, 0, _("Working directory not defined. Cannot continue.\n"));
635    }
636    if (stat(wd, &stat_buf) != 0) {
637       Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" not found. Cannot continue.\n"),
638          wd);
639    }
640    if (!S_ISDIR(stat_buf.st_mode)) {
641       Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" is not a directory. Cannot continue.\n"),
642          wd);
643    }
644    working_directory = wd;            /* set global */
645 }