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