]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/util.c
Update the Microsoft Visual Studio build to match the MinGW32 build.
[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 #if defined(HAVE_WIN32)
449    {
450       LARGE_INTEGER     li;
451       DWORD             length;
452       FILETIME          ft;
453       char             *p;
454
455       p = s;
456       sprintf(s + strlen(s), "%lu", (unsigned long)GetCurrentProcessId());
457       (void)getcwd(s + strlen(s), 256);
458       sprintf(s + strlen(s), "%lu", (unsigned long)GetTickCount());
459       QueryPerformanceCounter(&li);
460       sprintf(s + strlen(s), "%lu", (unsigned long)li.LowPart);
461       GetSystemTimeAsFileTime(&ft);
462       sprintf(s + strlen(s), "%lu", (unsigned long)ft.dwLowDateTime);
463       sprintf(s + strlen(s), "%lu", (unsigned long)ft.dwHighDateTime);
464       length = 256;
465       GetComputerName(s + strlen(s), &length);
466       length = 256;
467       GetUserName(s + strlen(s), &length);
468    }
469 #else
470    sprintf(s + strlen(s), "%lu", (unsigned long)getpid());
471    sprintf(s + strlen(s), "%lu", (unsigned long)getppid());
472    (void)getcwd(s + strlen(s), 256);
473    sprintf(s + strlen(s), "%lu", (unsigned long)clock());
474    sprintf(s + strlen(s), "%lu", (unsigned long)time(NULL));
475 #if defined(Solaris)
476    sysinfo(SI_HW_SERIAL,s + strlen(s), 12);
477 #endif
478 #if defined(HAVE_GETHOSTID)
479    sprintf(s + strlen(s), "%lu", (unsigned long) gethostid());
480 #endif
481    gethostname(s + strlen(s), 256);
482    sprintf(s + strlen(s), "%u", (unsigned)getuid());
483    sprintf(s + strlen(s), "%u", (unsigned)getgid());
484 #endif
485    MD5Init(&md5c);
486    MD5Update(&md5c, (unsigned char *)s, strlen(s));
487    MD5Final(md5key, &md5c);
488    sprintf(s + strlen(s), "%lu", (unsigned long)((time(NULL) + 65121) ^ 0x375F));
489    MD5Init(&md5c);
490    MD5Update(&md5c, (unsigned char *)s, strlen(s));
491    MD5Final(md5key1, &md5c);
492 #define nextrand    (md5key[j] ^ md5key1[j])
493    if (mode) {
494      for (j = k = 0; j < 16; j++) {
495         unsigned char rb = nextrand;
496
497 #define Rad16(x) ((x) + 'A')
498         key[k++] = Rad16((rb >> 4) & 0xF);
499         key[k++] = Rad16(rb & 0xF);
500 #undef Rad16
501         if (j & 1) {
502            key[k++] = '-';
503         }
504      }
505      key[--k] = 0;
506    } else {
507      for (j = 0; j < 16; j++) {
508         key[j] = nextrand;
509      }
510    }
511 }
512 #undef nextrand
513
514
515
516 /*
517  * Edit job codes into main command line
518  *  %% = %
519  *  %c = Client's name
520  *  %d = Director's name
521  *  %e = Job Exit code
522  *  %i = JobId
523  *  %j = Unique Job id
524  *  %l = job level
525  *  %n = Unadorned Job name
526  *  %s = Since time
527  *  %t = Job type (Backup, ...)
528  *  %r = Recipients
529  *  %v = Volume name
530  *
531  *  omsg = edited output message
532  *  imsg = input string containing edit codes (%x)
533  *  to = recepients list
534  *
535  */
536 POOLMEM *edit_job_codes(JCR *jcr, char *omsg, char *imsg, const char *to)
537 {
538    char *p, *q;
539    const char *str;
540    char add[20];
541    char name[MAX_NAME_LENGTH];
542    int i;
543
544    *omsg = 0;
545    Dmsg1(200, "edit_job_codes: %s\n", imsg);
546    for (p=imsg; *p; p++) {
547       if (*p == '%') {
548          switch (*++p) {
549          case '%':
550             str = "%";
551             break;
552          case 'c':
553             if (jcr) {
554                str = jcr->client_name;
555             } else {
556                str = _("*none*");
557             }
558             break;
559          case 'd':
560             str = my_name;            /* Director's name */
561             break;
562          case 'e':
563             if (jcr) {
564                str = job_status_to_str(jcr->JobStatus);
565             } else {
566                str = _("*none*");
567             }
568             break;
569          case 'i':
570             if (jcr) {
571                bsnprintf(add, sizeof(add), "%d", jcr->JobId);
572                str = add;
573             } else {
574                str = _("*none*");
575             }
576             break;
577          case 'j':                    /* Job name */
578             if (jcr) {
579                str = jcr->Job;
580             } else {
581                str = _("*none*");
582             }
583             break;
584          case 'l':
585             if (jcr) {
586                str = job_level_to_str(jcr->JobLevel);
587             } else {
588                str = _("*none*");
589             }
590             break;
591          case 'n':
592              if (jcr) {
593                 bstrncpy(name, jcr->Job, sizeof(name));
594                 /* There are three periods after the Job name */
595                 for (i=0; i<3; i++) {
596                    if ((q=strrchr(name, '.')) != NULL) {
597                        *q = 0;
598                    }
599                 }
600                 str = name;
601              } else {
602                 str = _("*none*");
603              }
604              break;
605          case 'r':
606             str = to;
607             break;
608          case 's':                    /* since time */
609             if (jcr && jcr->stime) {
610                str = jcr->stime;
611             } else {
612                str = _("*none*");
613             }
614             break;
615          case 't':
616             if (jcr) {
617                str = job_type_to_str(jcr->JobType);
618             } else {
619                str = _("*none*");
620             }
621             break;
622          case 'v':
623             if (jcr) {
624                if (jcr->VolumeName && jcr->VolumeName[0]) {
625                   str = jcr->VolumeName;
626                } else {
627                   str = "";
628                }
629             } else {
630                str = _("*none*");
631             }
632             break;
633          default:
634             add[0] = '%';
635             add[1] = *p;
636             add[2] = 0;
637             str = add;
638             break;
639          }
640       } else {
641          add[0] = *p;
642          add[1] = 0;
643          str = add;
644       }
645       Dmsg1(1200, "add_str %s\n", str);
646       pm_strcat(&omsg, str);
647       Dmsg1(1200, "omsg=%s\n", omsg);
648    }
649    return omsg;
650 }
651
652 void set_working_directory(char *wd)
653 {
654    struct stat stat_buf;
655
656    if (wd == NULL) {
657       Emsg0(M_ERROR_TERM, 0, _("Working directory not defined. Cannot continue.\n"));
658    }
659    if (stat(wd, &stat_buf) != 0) {
660       Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" not found. Cannot continue.\n"),
661          wd);
662    }
663    if (!S_ISDIR(stat_buf.st_mode)) {
664       Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" is not a directory. Cannot continue.\n"),
665          wd);
666    }
667    working_directory = wd;            /* set global */
668 }