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