]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/util.c
- Add a kludge to detect bad date/times, which cause a seg fault in
[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-2004 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;
42    char *p;
43    int i, len64, done, rem;
44
45    if (buf[0] != 0) {
46       return 0;
47    }
48    ip = (uint64_t *)buf;
49    /* Optimize by checking uint64_t for zero */
50    len64 = len / sizeof(uint64_t);
51    for (i=0; i < len64; i++) {
52       if (ip[i] != 0) {
53          return 0;
54       }
55    }
56    done = len64 * sizeof(uint64_t);  /* bytes already checked */
57    p = buf + done;
58    rem = len - done;
59    for (i = 0; i < rem; i++) {
60       if (p[i] != 0) {
61          return 0;
62       }
63    }
64    return 1;
65 }
66
67
68 /* Convert a string in place to lower case */
69 void lcase(char *str)
70 {
71    while (*str) {
72       if (B_ISUPPER(*str))
73          *str = tolower((int)(*str));
74        str++;
75    }
76 }
77
78 /* Convert spaces to non-space character. 
79  * This makes scanf of fields containing spaces easier.
80  */
81 void
82 bash_spaces(char *str)
83 {
84    while (*str) {
85       if (*str == ' ')
86          *str = 0x1;
87       str++;
88    }
89 }
90
91 /* Convert spaces to non-space character. 
92  * This makes scanf of fields containing spaces easier.
93  */
94 void
95 bash_spaces(POOL_MEM &pm)
96 {
97    char *str = pm.c_str();
98    while (*str) {
99       if (*str == ' ')
100          *str = 0x1;
101       str++;
102    }
103 }
104
105
106 /* Convert non-space characters (0x1) back into spaces */
107 void
108 unbash_spaces(char *str)
109 {
110    while (*str) {
111      if (*str == 0x1)
112         *str = ' ';
113      str++;
114    }
115 }
116
117 /* Convert non-space characters (0x1) back into spaces */
118 void
119 unbash_spaces(POOL_MEM &pm)
120 {
121    char *str = pm.c_str();
122    while (*str) {
123      if (*str == 0x1)
124         *str = ' ';
125      str++;
126    }
127 }
128
129 #ifdef WIN32
130 extern long _timezone;
131 extern int _daylight;
132 extern long _dstbias;
133 extern "C" void __tzset(void);
134 extern "C" int _isindst(struct tm *);
135 #endif
136
137 char *encode_time(time_t time, char *buf)
138 {
139    struct tm tm;
140    int n = 0;
141
142 #ifdef WIN32
143     /*
144      * Gross kludge to avoid a seg fault in Microsoft's CRT localtime_r(),
145      *  which incorrectly references a NULL returned from gmtime() if
146      *  the time (adjusted for the current timezone) is invalid.
147      *  This could happen if you have a bad date/time, or perhaps if you
148      *  moved a file from one timezone to another?
149      */
150     struct tm *gtm;
151     time_t gtime;
152     __tzset();
153     gtime = time - _timezone;
154     if (!(gtm = gmtime(&gtime))) {
155        return buf;
156     }
157     if (_daylight && _isindst(gtm)) {
158        gtime -= _dstbias;
159        if (!gmtime(&gtime)) {
160           return buf;
161        }
162     }
163 #endif
164    if (localtime_r(&time, &tm)) {
165       n = sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d",
166                    tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
167                    tm.tm_hour, tm.tm_min, tm.tm_sec);
168    }
169    return buf+n;
170 }
171
172
173
174 /*
175  * Convert a JobStatus code into a human readable form
176  */
177 void jobstatus_to_ascii(int JobStatus, char *msg, int maxlen)
178 {
179    const char *jobstat;
180    char buf[100];
181
182    switch (JobStatus) {
183    case JS_Created:
184       jobstat = _("Created");
185       break;
186    case JS_Running:
187       jobstat = _("Running");
188       break;
189    case JS_Blocked:
190       jobstat = _("Blocked");
191       break;
192    case JS_Terminated:
193       jobstat = _("OK");
194       break;
195    case JS_FatalError:
196    case JS_ErrorTerminated:
197       jobstat = _("Error");
198       break;
199    case JS_Error:
200       jobstat = _("Non-fatal error");
201       break;
202    case JS_Canceled:
203       jobstat = _("Canceled");
204       break;
205    case JS_Differences:
206       jobstat = _("Verify differences");
207       break;
208    case JS_WaitFD:
209       jobstat = _("Waiting on FD");
210       break;
211    case JS_WaitSD:
212       jobstat = _("Wait on SD");
213       break;
214    case JS_WaitMedia:
215       jobstat = _("Wait for new Volume");
216       break;
217    case JS_WaitMount:
218       jobstat = _("Waiting for mount");
219       break;
220    case JS_WaitStoreRes:
221       jobstat = _("Waiting for Storage resource");
222       break;
223    case JS_WaitJobRes:
224       jobstat = _("Waiting for Job resource");
225       break;
226    case JS_WaitClientRes:
227       jobstat = _("Waiting for Client resource");
228       break;
229    case JS_WaitMaxJobs:
230       jobstat = _("Waiting on Max Jobs");
231       break;
232    case JS_WaitStartTime:
233       jobstat = _("Waiting for Start Time");
234       break;
235    case JS_WaitPriority:
236       jobstat = _("Waiting on Priority");
237       break;
238
239    default:
240       if (JobStatus == 0) {
241          buf[0] = 0;
242       } else {
243          bsnprintf(buf, sizeof(buf), _("Unknown Job termination status=%d"), JobStatus);
244       }
245       jobstat = buf;
246       break;
247    }
248    bstrncpy(msg, jobstat, maxlen);
249 }
250
251 /*
252  * Convert Job Termination Status into a string
253  */
254 const char *job_status_to_str(int stat) 
255 {
256    const char *str;
257
258    switch (stat) {
259    case JS_Terminated:
260       str = _("OK");
261       break;
262    case JS_ErrorTerminated:
263    case JS_Error:
264       str = _("Error");
265       break;
266    case JS_FatalError:
267       str = _("Fatal Error");
268       break;
269    case JS_Canceled:
270       str = _("Canceled");
271       break;
272    case JS_Differences:
273       str = _("Differences");
274       break;
275    default:
276       str = _("Unknown term code");
277       break;
278    }
279    return str;
280 }
281
282
283 /*
284  * Convert Job Type into a string
285  */
286 const char *job_type_to_str(int type) 
287 {
288    const char *str;
289
290    switch (type) {
291    case JT_BACKUP:
292       str = _("Backup");
293       break;
294    case JT_VERIFY:
295       str = _("Verify");
296       break;
297    case JT_RESTORE:
298       str = _("Restore");
299       break;
300    case JT_ADMIN:
301       str = _("Admin");
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      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 #ifdef HAVE_GETDOMAINNAME
473      getdomainname(s + strlen(s), 256);
474 #endif
475      gethostname(s + strlen(s), 256);
476      sprintf(s + strlen(s), "%u", (unsigned)getuid());
477      sprintf(s + strlen(s), "%u", (unsigned)getgid());
478      MD5Init(&md5c);
479      MD5Update(&md5c, (unsigned char *)s, strlen(s));
480      MD5Final(md5key, &md5c);
481      sprintf(s + strlen(s), "%lu", (unsigned long)((time(NULL) + 65121) ^ 0x375F));
482      MD5Init(&md5c);
483      MD5Update(&md5c, (unsigned char *)s, strlen(s));
484      MD5Final(md5key1, &md5c);
485 #define nextrand    (md5key[j] ^ md5key1[j])
486      if (mode) {
487         for (j = k = 0; j < 16; j++) {
488            unsigned char rb = nextrand;
489
490 #define Rad16(x) ((x) + 'A')
491            key[k++] = Rad16((rb >> 4) & 0xF);
492            key[k++] = Rad16(rb & 0xF);
493 #undef Rad16
494            if (j & 1) {
495               key[k++] = '-';
496            }
497         }
498         key[--k] = 0;
499      } else {
500         for (j = 0; j < 16; j++) {
501            key[j] = nextrand;
502         }
503      }
504 }
505 #undef nextrand
506
507
508
509 /*
510  * Edit job codes into main command line
511  *  %% = %
512  *  %c = Client's name
513  *  %d = Director's name
514  *  %e = Job Exit code
515  *  %i = JobId
516  *  %j = Unique Job name
517  *  %l = job level
518  *  %n = Unadorned Job name
519  *  %t = Job type (Backup, ...)
520  *  %r = Recipients
521  *  %v = Volume name
522  *
523  *  omsg = edited output message
524  *  imsg = input string containing edit codes (%x)
525  *  to = recepients list 
526  *
527  */
528 POOLMEM *edit_job_codes(JCR *jcr, char *omsg, char *imsg, const char *to)   
529 {
530    char *p, *q;
531    const char *str;
532    char add[20];
533    char name[MAX_NAME_LENGTH];
534    int i;
535
536    *omsg = 0;
537    Dmsg1(200, "edit_job_codes: %s\n", imsg);
538    for (p=imsg; *p; p++) {
539       if (*p == '%') {
540          switch (*++p) {
541          case '%':
542             str = "%";
543             break;
544          case 'c':
545             str = jcr->client_name;
546             if (!str) {
547                str = "";
548             }
549             break;
550          case 'd':
551             str = my_name;            /* Director's name */
552             break;
553          case 'e':
554             str = job_status_to_str(jcr->JobStatus); 
555             break;
556          case 'i':
557             bsnprintf(add, sizeof(add), "%d", jcr->JobId);
558             str = add;
559             break;
560          case 'j':                    /* Job name */
561             str = jcr->Job;
562             break;
563          case 'l':
564             str = job_level_to_str(jcr->JobLevel);
565             break;
566          case 'n':
567              bstrncpy(name, jcr->Job, sizeof(name));
568              /* There are three periods after the Job name */
569              for (i=0; i<3; i++) {
570                 if ((q=strrchr(name, '.')) != NULL) {
571                     *q = 0;
572                 }
573              }
574              str = name;
575              break;
576          case 'r':
577             str = to;
578             break;
579          case 't':
580             str = job_type_to_str(jcr->JobType);
581             break;
582          case 'v':
583             if (jcr->VolumeName && jcr->VolumeName[0]) {
584                str = jcr->VolumeName;
585             } else {
586                str = "";
587             }
588             break;
589          default:
590             add[0] = '%';
591             add[1] = *p;
592             add[2] = 0;
593             str = add;
594             break;
595          }
596       } else {
597          add[0] = *p;
598          add[1] = 0;
599          str = add;
600       }
601       Dmsg1(1200, "add_str %s\n", str);
602       pm_strcat(&omsg, str);
603       Dmsg1(1200, "omsg=%s\n", omsg);
604    }
605    return omsg;
606 }
607
608 void set_working_directory(char *wd)
609 {
610    struct stat stat_buf; 
611
612    if (wd == NULL) {
613       Emsg0(M_ERROR_TERM, 0, _("Working directory not defined. Cannot continue.\n"));
614    }
615    if (stat(wd, &stat_buf) != 0) {
616       Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" not found. Cannot continue.\n"),
617          wd);
618    }
619    if (!S_ISDIR(stat_buf.st_mode)) {
620       Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" is not a directory. Cannot continue.\n"),
621          wd);
622    }
623    working_directory = wd;            /* set global */
624 }