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