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