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