]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/util.c
kes Require a messages command acl to be able to receive messages
[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
384 int do_shell_expansion(char *name, int name_len)
385 {
386    static char meta[] = "~\\$[]*?`'<>\"";
387    bool found = false;
388    int len, i, stat;
389    POOLMEM *cmd;
390    BPIPE *bpipe;
391    char line[MAXSTRING];
392    const char *shellcmd;
393
394    /* Check if any meta characters are present */
395    len = strlen(meta);
396    for (i = 0; i < len; i++) {
397       if (strchr(name, meta[i])) {
398          found = true;
399          break;
400       }
401    }
402    if (found) {
403       cmd =  get_pool_memory(PM_FNAME);
404       /* look for shell */
405       if ((shellcmd = getenv("SHELL")) == NULL) {
406          shellcmd = "/bin/sh";
407       }
408       pm_strcpy(&cmd, shellcmd);
409       pm_strcat(&cmd, " -c \"echo ");
410       pm_strcat(&cmd, name);
411       pm_strcat(&cmd, "\"");
412       Dmsg1(400, "Send: %s\n", cmd);
413       if ((bpipe = open_bpipe(cmd, 0, "r"))) {
414          *line = 0;
415          fgets(line, sizeof(line), bpipe->rfd);
416          strip_trailing_junk(line);
417          stat = close_bpipe(bpipe);
418          Dmsg2(400, "stat=%d got: %s\n", stat, line);
419       } else {
420          stat = 1;                    /* error */
421       }
422       free_pool_memory(cmd);
423       if (stat == 0) {
424          bstrncpy(name, line, name_len);
425       }
426    }
427    return 1;
428 }
429
430
431 /*  MAKESESSIONKEY  --  Generate session key with optional start
432                         key.  If mode is TRUE, the key will be
433                         translated to a string, otherwise it is
434                         returned as 16 binary bytes.
435
436     from SpeakFreely by John Walker */
437
438 void make_session_key(char *key, char *seed, int mode)
439 {
440    int j, k;
441    struct MD5Context md5c;
442    unsigned char md5key[16], md5key1[16];
443    char s[1024];
444
445    s[0] = 0;
446    if (seed != NULL) {
447      bstrncat(s, seed, sizeof(s));
448    }
449
450    /* The following creates a seed for the session key generator
451      based on a collection of volatile and environment-specific
452      information unlikely to be vulnerable (as a whole) to an
453      exhaustive search attack.  If one of these items isn't
454      available on your machine, replace it with something
455      equivalent or, if you like, just delete it. */
456
457 #if defined(HAVE_WIN32)
458    {
459       LARGE_INTEGER     li;
460       DWORD             length;
461       FILETIME          ft;
462       char             *p;
463
464       p = s;
465       sprintf(s + strlen(s), "%lu", (unsigned long)GetCurrentProcessId());
466       (void)getcwd(s + strlen(s), 256);
467       sprintf(s + strlen(s), "%lu", (unsigned long)GetTickCount());
468       QueryPerformanceCounter(&li);
469       sprintf(s + strlen(s), "%lu", (unsigned long)li.LowPart);
470       GetSystemTimeAsFileTime(&ft);
471       sprintf(s + strlen(s), "%lu", (unsigned long)ft.dwLowDateTime);
472       sprintf(s + strlen(s), "%lu", (unsigned long)ft.dwHighDateTime);
473       length = 256;
474       GetComputerName(s + strlen(s), &length);
475       length = 256;
476       GetUserName(s + strlen(s), &length);
477    }
478 #else
479    sprintf(s + strlen(s), "%lu", (unsigned long)getpid());
480    sprintf(s + strlen(s), "%lu", (unsigned long)getppid());
481    (void)getcwd(s + strlen(s), 256);
482    sprintf(s + strlen(s), "%lu", (unsigned long)clock());
483    sprintf(s + strlen(s), "%lu", (unsigned long)time(NULL));
484 #if defined(Solaris)
485    sysinfo(SI_HW_SERIAL,s + strlen(s), 12);
486 #endif
487 #if defined(HAVE_GETHOSTID)
488    sprintf(s + strlen(s), "%lu", (unsigned long) gethostid());
489 #endif
490    gethostname(s + strlen(s), 256);
491    sprintf(s + strlen(s), "%u", (unsigned)getuid());
492    sprintf(s + strlen(s), "%u", (unsigned)getgid());
493 #endif
494    MD5Init(&md5c);
495    MD5Update(&md5c, (unsigned char *)s, strlen(s));
496    MD5Final(md5key, &md5c);
497    sprintf(s + strlen(s), "%lu", (unsigned long)((time(NULL) + 65121) ^ 0x375F));
498    MD5Init(&md5c);
499    MD5Update(&md5c, (unsigned char *)s, strlen(s));
500    MD5Final(md5key1, &md5c);
501 #define nextrand    (md5key[j] ^ md5key1[j])
502    if (mode) {
503      for (j = k = 0; j < 16; j++) {
504         unsigned char rb = nextrand;
505
506 #define Rad16(x) ((x) + 'A')
507         key[k++] = Rad16((rb >> 4) & 0xF);
508         key[k++] = Rad16(rb & 0xF);
509 #undef Rad16
510         if (j & 1) {
511            key[k++] = '-';
512         }
513      }
514      key[--k] = 0;
515    } else {
516      for (j = 0; j < 16; j++) {
517         key[j] = nextrand;
518      }
519    }
520 }
521 #undef nextrand
522
523
524
525 /*
526  * Edit job codes into main command line
527  *  %% = %
528  *  %c = Client's name
529  *  %d = Director's name
530  *  %e = Job Exit code
531  *  %i = JobId
532  *  %j = Unique Job id
533  *  %l = job level
534  *  %n = Unadorned Job name
535  *  %s = Since time
536  *  %t = Job type (Backup, ...)
537  *  %r = Recipients
538  *  %v = Volume name
539  *
540  *  omsg = edited output message
541  *  imsg = input string containing edit codes (%x)
542  *  to = recepients list
543  *
544  */
545 POOLMEM *edit_job_codes(JCR *jcr, char *omsg, char *imsg, const char *to)
546 {
547    char *p, *q;
548    const char *str;
549    char add[20];
550    char name[MAX_NAME_LENGTH];
551    int i;
552
553    *omsg = 0;
554    Dmsg1(200, "edit_job_codes: %s\n", imsg);
555    for (p=imsg; *p; p++) {
556       if (*p == '%') {
557          switch (*++p) {
558          case '%':
559             str = "%";
560             break;
561          case 'c':
562             if (jcr) {
563                str = jcr->client_name;
564             } else {
565                str = _("*none*");
566             }
567             break;
568          case 'd':
569             str = my_name;            /* Director's name */
570             break;
571          case 'e':
572             if (jcr) {
573                str = job_status_to_str(jcr->JobStatus);
574             } else {
575                str = _("*none*");
576             }
577             break;
578          case 'i':
579             if (jcr) {
580                bsnprintf(add, sizeof(add), "%d", jcr->JobId);
581                str = add;
582             } else {
583                str = _("*none*");
584             }
585             break;
586          case 'j':                    /* Job name */
587             if (jcr) {
588                str = jcr->Job;
589             } else {
590                str = _("*none*");
591             }
592             break;
593          case 'l':
594             if (jcr) {
595                str = job_level_to_str(jcr->JobLevel);
596             } else {
597                str = _("*none*");
598             }
599             break;
600          case 'n':
601              if (jcr) {
602                 bstrncpy(name, jcr->Job, sizeof(name));
603                 /* There are three periods after the Job name */
604                 for (i=0; i<3; i++) {
605                    if ((q=strrchr(name, '.')) != NULL) {
606                        *q = 0;
607                    }
608                 }
609                 str = name;
610              } else {
611                 str = _("*none*");
612              }
613              break;
614          case 'r':
615             str = to;
616             break;
617          case 's':                    /* since time */
618             if (jcr && jcr->stime) {
619                str = jcr->stime;
620             } else {
621                str = _("*none*");
622             }
623             break;
624          case 't':
625             if (jcr) {
626                str = job_type_to_str(jcr->JobType);
627             } else {
628                str = _("*none*");
629             }
630             break;
631          case 'v':
632             if (jcr) {
633                if (jcr->VolumeName && jcr->VolumeName[0]) {
634                   str = jcr->VolumeName;
635                } else {
636                   str = "";
637                }
638             } else {
639                str = _("*none*");
640             }
641             break;
642          default:
643             add[0] = '%';
644             add[1] = *p;
645             add[2] = 0;
646             str = add;
647             break;
648          }
649       } else {
650          add[0] = *p;
651          add[1] = 0;
652          str = add;
653       }
654       Dmsg1(1200, "add_str %s\n", str);
655       pm_strcat(&omsg, str);
656       Dmsg1(1200, "omsg=%s\n", omsg);
657    }
658    return omsg;
659 }
660
661 void set_working_directory(char *wd)
662 {
663    struct stat stat_buf;
664
665    if (wd == NULL) {
666       Emsg0(M_ERROR_TERM, 0, _("Working directory not defined. Cannot continue.\n"));
667    }
668    if (stat(wd, &stat_buf) != 0) {
669       Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" not found. Cannot continue.\n"),
670          wd);
671    }
672    if (!S_ISDIR(stat_buf.st_mode)) {
673       Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" is not a directory. Cannot continue.\n"),
674          wd);
675    }
676    working_directory = wd;            /* set global */
677 }