]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/util.c
Ignore UTF-8 marker at the start of .conf files.
[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    case JT_CONSOLE:
291       str = _("Console");
292       break;
293    case JT_SYSTEM:
294       str = _("System or Console");
295       break;
296    case JT_SCAN:
297       str = _("Scan");
298       break;
299    default:
300       str = _("Unknown Type");
301       break;
302    }
303    return str;
304 }
305
306 /*
307  * Convert Job Level into a string
308  */
309 const char *job_level_to_str(int level)
310 {
311    const char *str;
312
313    switch (level) {
314    case L_BASE:
315       str = _("Base");
316    case L_FULL:
317       str = _("Full");
318       break;
319    case L_INCREMENTAL:
320       str = _("Incremental");
321       break;
322    case L_DIFFERENTIAL:
323       str = _("Differential");
324       break;
325    case L_SINCE:
326       str = _("Since");
327       break;
328    case L_VERIFY_CATALOG:
329       str = _("Verify Catalog");
330       break;
331    case L_VERIFY_INIT:
332       str = _("Verify Init Catalog");
333       break;
334    case L_VERIFY_VOLUME_TO_CATALOG:
335       str = _("Verify Volume to Catalog");
336       break;
337    case L_VERIFY_DISK_TO_CATALOG:
338       str = _("Verify Disk to Catalog");
339       break;
340    case L_VERIFY_DATA:
341       str = _("Verify Data");
342       break;
343    case L_NONE:
344       str = " ";
345       break;
346    default:
347       str = _("Unknown Job Level");
348       break;
349    }
350    return str;
351 }
352
353
354 /***********************************************************************
355  * Encode the mode bits into a 10 character string like LS does
356  ***********************************************************************/
357
358 char *encode_mode(mode_t mode, char *buf)
359 {
360   char *cp = buf;
361
362   *cp++ = S_ISDIR(mode) ? 'd' : S_ISBLK(mode)  ? 'b' : S_ISCHR(mode)  ? 'c' :
363           S_ISLNK(mode) ? 'l' : S_ISFIFO(mode) ? 'f' : S_ISSOCK(mode) ? 's' : '-';
364   *cp++ = mode & S_IRUSR ? 'r' : '-';
365   *cp++ = mode & S_IWUSR ? 'w' : '-';
366   *cp++ = (mode & S_ISUID
367                ? (mode & S_IXUSR ? 's' : 'S')
368                : (mode & S_IXUSR ? 'x' : '-'));
369   *cp++ = mode & S_IRGRP ? 'r' : '-';
370   *cp++ = mode & S_IWGRP ? 'w' : '-';
371   *cp++ = (mode & S_ISGID
372                ? (mode & S_IXGRP ? 's' : 'S')
373                : (mode & S_IXGRP ? 'x' : '-'));
374   *cp++ = mode & S_IROTH ? 'r' : '-';
375   *cp++ = mode & S_IWOTH ? 'w' : '-';
376   *cp++ = (mode & S_ISVTX
377                ? (mode & S_IXOTH ? 't' : 'T')
378                : (mode & S_IXOTH ? 'x' : '-'));
379   *cp = '\0';
380   return cp;
381 }
382
383 #if defined(HAVE_WIN32)
384 int do_shell_expansion(char *name, int name_len)
385 {
386    char *src = bstrdup(name);
387
388    ExpandEnvironmentStrings(src, name, name_len);
389
390    free(src);
391
392    return 1;
393 }
394 #else
395 int do_shell_expansion(char *name, int name_len)
396 {
397    static char meta[] = "~\\$[]*?`'<>\"";
398    bool found = false;
399    int len, i, stat;
400    POOLMEM *cmd;
401    BPIPE *bpipe;
402    char line[MAXSTRING];
403    const char *shellcmd;
404
405    /* Check if any meta characters are present */
406    len = strlen(meta);
407    for (i = 0; i < len; i++) {
408       if (strchr(name, meta[i])) {
409          found = true;
410          break;
411       }
412    }
413    if (found) {
414       cmd =  get_pool_memory(PM_FNAME);
415       /* look for shell */
416       if ((shellcmd = getenv("SHELL")) == NULL) {
417          shellcmd = "/bin/sh";
418       }
419       pm_strcpy(&cmd, shellcmd);
420       pm_strcat(&cmd, " -c \"echo ");
421       pm_strcat(&cmd, name);
422       pm_strcat(&cmd, "\"");
423       Dmsg1(400, "Send: %s\n", cmd);
424       if ((bpipe = open_bpipe(cmd, 0, "r"))) {
425          *line = 0;
426          fgets(line, sizeof(line), bpipe->rfd);
427          strip_trailing_junk(line);
428          stat = close_bpipe(bpipe);
429          Dmsg2(400, "stat=%d got: %s\n", stat, line);
430       } else {
431          stat = 1;                    /* error */
432       }
433       free_pool_memory(cmd);
434       if (stat == 0) {
435          bstrncpy(name, line, name_len);
436       }
437    }
438    return 1;
439 }
440 #endif
441
442
443 /*  MAKESESSIONKEY  --  Generate session key with optional start
444                         key.  If mode is TRUE, the key will be
445                         translated to a string, otherwise it is
446                         returned as 16 binary bytes.
447
448     from SpeakFreely by John Walker */
449
450 void make_session_key(char *key, char *seed, int mode)
451 {
452    int j, k;
453    struct MD5Context md5c;
454    unsigned char md5key[16], md5key1[16];
455    char s[1024];
456
457    s[0] = 0;
458    if (seed != NULL) {
459      bstrncat(s, seed, sizeof(s));
460    }
461
462    /* The following creates a seed for the session key generator
463      based on a collection of volatile and environment-specific
464      information unlikely to be vulnerable (as a whole) to an
465      exhaustive search attack.  If one of these items isn't
466      available on your machine, replace it with something
467      equivalent or, if you like, just delete it. */
468
469 #if defined(HAVE_WIN32)
470    {
471       LARGE_INTEGER     li;
472       DWORD             length;
473       FILETIME          ft;
474       char             *p;
475
476       p = s;
477       sprintf(s + strlen(s), "%lu", (unsigned long)GetCurrentProcessId());
478       (void)getcwd(s + strlen(s), 256);
479       sprintf(s + strlen(s), "%lu", (unsigned long)GetTickCount());
480       QueryPerformanceCounter(&li);
481       sprintf(s + strlen(s), "%lu", (unsigned long)li.LowPart);
482       GetSystemTimeAsFileTime(&ft);
483       sprintf(s + strlen(s), "%lu", (unsigned long)ft.dwLowDateTime);
484       sprintf(s + strlen(s), "%lu", (unsigned long)ft.dwHighDateTime);
485       length = 256;
486       GetComputerName(s + strlen(s), &length);
487       length = 256;
488       GetUserName(s + strlen(s), &length);
489    }
490 #else
491    sprintf(s + strlen(s), "%lu", (unsigned long)getpid());
492    sprintf(s + strlen(s), "%lu", (unsigned long)getppid());
493    (void)getcwd(s + strlen(s), 256);
494    sprintf(s + strlen(s), "%lu", (unsigned long)clock());
495    sprintf(s + strlen(s), "%lu", (unsigned long)time(NULL));
496 #if defined(Solaris)
497    sysinfo(SI_HW_SERIAL,s + strlen(s), 12);
498 #endif
499 #if defined(HAVE_GETHOSTID)
500    sprintf(s + strlen(s), "%lu", (unsigned long) gethostid());
501 #endif
502    gethostname(s + strlen(s), 256);
503    sprintf(s + strlen(s), "%u", (unsigned)getuid());
504    sprintf(s + strlen(s), "%u", (unsigned)getgid());
505 #endif
506    MD5Init(&md5c);
507    MD5Update(&md5c, (unsigned char *)s, strlen(s));
508    MD5Final(md5key, &md5c);
509    sprintf(s + strlen(s), "%lu", (unsigned long)((time(NULL) + 65121) ^ 0x375F));
510    MD5Init(&md5c);
511    MD5Update(&md5c, (unsigned char *)s, strlen(s));
512    MD5Final(md5key1, &md5c);
513 #define nextrand    (md5key[j] ^ md5key1[j])
514    if (mode) {
515      for (j = k = 0; j < 16; j++) {
516         unsigned char rb = nextrand;
517
518 #define Rad16(x) ((x) + 'A')
519         key[k++] = Rad16((rb >> 4) & 0xF);
520         key[k++] = Rad16(rb & 0xF);
521 #undef Rad16
522         if (j & 1) {
523            key[k++] = '-';
524         }
525      }
526      key[--k] = 0;
527    } else {
528      for (j = 0; j < 16; j++) {
529         key[j] = nextrand;
530      }
531    }
532 }
533 #undef nextrand
534
535
536
537 /*
538  * Edit job codes into main command line
539  *  %% = %
540  *  %c = Client's name
541  *  %d = Director's name
542  *  %e = Job Exit code
543  *  %i = JobId
544  *  %j = Unique Job id
545  *  %l = job level
546  *  %n = Unadorned Job name
547  *  %s = Since time
548  *  %t = Job type (Backup, ...)
549  *  %r = Recipients
550  *  %v = Volume name
551  *
552  *  omsg = edited output message
553  *  imsg = input string containing edit codes (%x)
554  *  to = recepients list
555  *
556  */
557 POOLMEM *edit_job_codes(JCR *jcr, char *omsg, char *imsg, const char *to)
558 {
559    char *p, *q;
560    const char *str;
561    char add[20];
562    char name[MAX_NAME_LENGTH];
563    int i;
564
565    *omsg = 0;
566    Dmsg1(200, "edit_job_codes: %s\n", imsg);
567    for (p=imsg; *p; p++) {
568       if (*p == '%') {
569          switch (*++p) {
570          case '%':
571             str = "%";
572             break;
573          case 'c':
574             if (jcr) {
575                str = jcr->client_name;
576             } else {
577                str = _("*none*");
578             }
579             break;
580          case 'd':
581             str = my_name;            /* Director's name */
582             break;
583          case 'e':
584             if (jcr) {
585                str = job_status_to_str(jcr->JobStatus);
586             } else {
587                str = _("*none*");
588             }
589             break;
590          case 'i':
591             if (jcr) {
592                bsnprintf(add, sizeof(add), "%d", jcr->JobId);
593                str = add;
594             } else {
595                str = _("*none*");
596             }
597             break;
598          case 'j':                    /* Job name */
599             if (jcr) {
600                str = jcr->Job;
601             } else {
602                str = _("*none*");
603             }
604             break;
605          case 'l':
606             if (jcr) {
607                str = job_level_to_str(jcr->JobLevel);
608             } else {
609                str = _("*none*");
610             }
611             break;
612          case 'n':
613              if (jcr) {
614                 bstrncpy(name, jcr->Job, sizeof(name));
615                 /* There are three periods after the Job name */
616                 for (i=0; i<3; i++) {
617                    if ((q=strrchr(name, '.')) != NULL) {
618                        *q = 0;
619                    }
620                 }
621                 str = name;
622              } else {
623                 str = _("*none*");
624              }
625              break;
626          case 'r':
627             str = to;
628             break;
629          case 's':                    /* since time */
630             if (jcr && jcr->stime) {
631                str = jcr->stime;
632             } else {
633                str = _("*none*");
634             }
635             break;
636          case 't':
637             if (jcr) {
638                str = job_type_to_str(jcr->JobType);
639             } else {
640                str = _("*none*");
641             }
642             break;
643          case 'v':
644             if (jcr) {
645                if (jcr->VolumeName && jcr->VolumeName[0]) {
646                   str = jcr->VolumeName;
647                } else {
648                   str = "";
649                }
650             } else {
651                str = _("*none*");
652             }
653             break;
654          default:
655             add[0] = '%';
656             add[1] = *p;
657             add[2] = 0;
658             str = add;
659             break;
660          }
661       } else {
662          add[0] = *p;
663          add[1] = 0;
664          str = add;
665       }
666       Dmsg1(1200, "add_str %s\n", str);
667       pm_strcat(&omsg, str);
668       Dmsg1(1200, "omsg=%s\n", omsg);
669    }
670    return omsg;
671 }
672
673 void set_working_directory(char *wd)
674 {
675    struct stat stat_buf;
676
677    if (wd == NULL) {
678       Emsg0(M_ERROR_TERM, 0, _("Working directory not defined. Cannot continue.\n"));
679    }
680    if (stat(wd, &stat_buf) != 0) {
681       Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" not found. Cannot continue.\n"),
682          wd);
683    }
684    if (!S_ISDIR(stat_buf.st_mode)) {
685       Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" is not a directory. Cannot continue.\n"),
686          wd);
687    }
688    working_directory = wd;            /* set global */
689 }