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