]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/util.c
03Dec07
[bacula/bacula] / bacula / src / lib / util.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2006 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of John Walker.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *   util.c  miscellaneous utility subroutines for Bacula
30  *
31  *    Kern Sibbald, MM
32  *
33  *   Version $Id$
34  */
35
36 #include "bacula.h"
37 #include "jcr.h"
38 #include "findlib/find.h"
39
40 /*
41  * Various Bacula Utility subroutines
42  *
43  */
44
45 /* Return true of buffer has all zero bytes */
46 bool is_buf_zero(char *buf, int len)
47 {
48    uint64_t *ip;
49    char *p;
50    int i, len64, done, rem;
51
52    if (buf[0] != 0) {
53       return false;
54    }
55    ip = (uint64_t *)buf;
56    /* Optimize by checking uint64_t for zero */
57    len64 = len / sizeof(uint64_t);
58    for (i=0; i < len64; i++) {
59       if (ip[i] != 0) {
60          return false;
61       }
62    }
63    done = len64 * sizeof(uint64_t);  /* bytes already checked */
64    p = buf + done;
65    rem = len - done;
66    for (i = 0; i < rem; i++) {
67       if (p[i] != 0) {
68          return false;
69       }
70    }
71    return true;
72 }
73
74
75 /* Convert a string in place to lower case */
76 void lcase(char *str)
77 {
78    while (*str) {
79       if (B_ISUPPER(*str)) {
80          *str = tolower((int)(*str));
81        }
82        str++;
83    }
84 }
85
86 /* Convert spaces to non-space character.
87  * This makes scanf of fields containing spaces easier.
88  */
89 void
90 bash_spaces(char *str)
91 {
92    while (*str) {
93       if (*str == ' ')
94          *str = 0x1;
95       str++;
96    }
97 }
98
99 /* Convert spaces to non-space character.
100  * This makes scanf of fields containing spaces easier.
101  */
102 void
103 bash_spaces(POOL_MEM &pm)
104 {
105    char *str = pm.c_str();
106    while (*str) {
107       if (*str == ' ')
108          *str = 0x1;
109       str++;
110    }
111 }
112
113
114 /* Convert non-space characters (0x1) back into spaces */
115 void
116 unbash_spaces(char *str)
117 {
118    while (*str) {
119      if (*str == 0x1)
120         *str = ' ';
121      str++;
122    }
123 }
124
125 /* Convert non-space characters (0x1) back into spaces */
126 void
127 unbash_spaces(POOL_MEM &pm)
128 {
129    char *str = pm.c_str();
130    while (*str) {
131      if (*str == 0x1)
132         *str = ' ';
133      str++;
134    }
135 }
136
137 char *encode_time(time_t time, char *buf)
138 {
139    struct tm tm;
140    int n = 0;
141
142 #if defined(HAVE_WIN32)
143    /*
144     * Avoid a seg fault in Microsoft's CRT localtime_r(),
145     *  which incorrectly references a NULL returned from gmtime() if
146     *  time is negative before or after the timezone adjustment.
147     */
148    struct tm *gtm;
149
150    if ((gtm = gmtime(&time)) == NULL) {
151       return buf;
152    }
153
154    if (gtm->tm_year == 1970 && gtm->tm_mon == 1 && gtm->tm_mday < 3) {
155       return buf;
156    }
157 #endif
158
159    if (localtime_r(&time, &tm)) {
160       n = sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d",
161                    tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
162                    tm.tm_hour, tm.tm_min, tm.tm_sec);
163    }
164    return buf+n;
165 }
166
167
168
169 /*
170  * Convert a JobStatus code into a human readable form
171  */
172 void jobstatus_to_ascii(int JobStatus, char *msg, int maxlen)
173 {
174    const char *jobstat;
175    char buf[100];
176
177    switch (JobStatus) {
178    case JS_Created:
179       jobstat = _("Created");
180       break;
181    case JS_Running:
182       jobstat = _("Running");
183       break;
184    case JS_Blocked:
185       jobstat = _("Blocked");
186       break;
187    case JS_Terminated:
188       jobstat = _("OK");
189       break;
190    case JS_FatalError:
191    case JS_ErrorTerminated:
192       jobstat = _("Error");
193       break;
194    case JS_Error:
195       jobstat = _("Non-fatal error");
196       break;
197    case JS_Canceled:
198       jobstat = _("Canceled");
199       break;
200    case JS_Differences:
201       jobstat = _("Verify differences");
202       break;
203    case JS_WaitFD:
204       jobstat = _("Waiting on FD");
205       break;
206    case JS_WaitSD:
207       jobstat = _("Wait on SD");
208       break;
209    case JS_WaitMedia:
210       jobstat = _("Wait for new Volume");
211       break;
212    case JS_WaitMount:
213       jobstat = _("Waiting for mount");
214       break;
215    case JS_WaitStoreRes:
216       jobstat = _("Waiting for Storage resource");
217       break;
218    case JS_WaitJobRes:
219       jobstat = _("Waiting for Job resource");
220       break;
221    case JS_WaitClientRes:
222       jobstat = _("Waiting for Client resource");
223       break;
224    case JS_WaitMaxJobs:
225       jobstat = _("Waiting on Max Jobs");
226       break;
227    case JS_WaitStartTime:
228       jobstat = _("Waiting for Start Time");
229       break;
230    case JS_WaitPriority:
231       jobstat = _("Waiting on Priority");
232       break;
233    case JS_AttrDespooling:
234       jobstat = _("SD despooling Attributes");
235       break;
236    case JS_AttrInserting:
237       jobstat = _("Dir inserting Attributes");
238       break;
239
240    default:
241       if (JobStatus == 0) {
242          buf[0] = 0;
243       } else {
244          bsnprintf(buf, sizeof(buf), _("Unknown Job termination status=%d"), JobStatus);
245       }
246       jobstat = buf;
247       break;
248    }
249    bstrncpy(msg, jobstat, maxlen);
250 }
251
252 /*
253  * Convert Job Termination Status into a string
254  */
255 const char *job_status_to_str(int stat)
256 {
257    const char *str;
258
259    switch (stat) {
260    case JS_Terminated:
261       str = _("OK");
262       break;
263    case JS_ErrorTerminated:
264    case JS_Error:
265       str = _("Error");
266       break;
267    case JS_FatalError:
268       str = _("Fatal Error");
269       break;
270    case JS_Canceled:
271       str = _("Canceled");
272       break;
273    case JS_Differences:
274       str = _("Differences");
275       break;
276    default:
277       str = _("Unknown term code");
278       break;
279    }
280    return str;
281 }
282
283
284 /*
285  * Convert Job Type into a string
286  */
287 const char *job_type_to_str(int type)
288 {
289    const char *str;
290
291    switch (type) {
292    case JT_BACKUP:
293       str = _("Backup");
294       break;
295    case JT_VERIFY:
296       str = _("Verify");
297       break;
298    case JT_RESTORE:
299       str = _("Restore");
300       break;
301    case JT_ADMIN:
302       str = _("Admin");
303       break;
304    case JT_MIGRATE:
305       str = _("Migrate");
306       break;
307    case JT_COPY:
308       str = _("Copy");
309       break;
310    case JT_CONSOLE:
311       str = _("Console");
312       break;
313    case JT_SYSTEM:
314       str = _("System or Console");
315       break;
316    case JT_SCAN:
317       str = _("Scan");
318       break;
319    default:
320       str = _("Unknown Type");
321       break;
322    }
323    return str;
324 }
325
326 /*
327  * Convert Job Level into a string
328  */
329 const char *job_level_to_str(int level)
330 {
331    const char *str;
332
333    switch (level) {
334    case L_BASE:
335       str = _("Base");
336    case L_FULL:
337       str = _("Full");
338       break;
339    case L_INCREMENTAL:
340       str = _("Incremental");
341       break;
342    case L_DIFFERENTIAL:
343       str = _("Differential");
344       break;
345    case L_SINCE:
346       str = _("Since");
347       break;
348    case L_VERIFY_CATALOG:
349       str = _("Verify Catalog");
350       break;
351    case L_VERIFY_INIT:
352       str = _("Verify Init Catalog");
353       break;
354    case L_VERIFY_VOLUME_TO_CATALOG:
355       str = _("Verify Volume to Catalog");
356       break;
357    case L_VERIFY_DISK_TO_CATALOG:
358       str = _("Verify Disk to Catalog");
359       break;
360    case L_VERIFY_DATA:
361       str = _("Verify Data");
362       break;
363    case L_NONE:
364       str = " ";
365       break;
366    default:
367       str = _("Unknown Job Level");
368       break;
369    }
370    return str;
371 }
372
373
374 /***********************************************************************
375  * Encode the mode bits into a 10 character string like LS does
376  ***********************************************************************/
377
378 char *encode_mode(mode_t mode, char *buf)
379 {
380   char *cp = buf;
381
382   *cp++ = S_ISDIR(mode) ? 'd' : S_ISBLK(mode)  ? 'b' : S_ISCHR(mode)  ? 'c' :
383           S_ISLNK(mode) ? 'l' : S_ISFIFO(mode) ? 'f' : S_ISSOCK(mode) ? 's' : '-';
384   *cp++ = mode & S_IRUSR ? 'r' : '-';
385   *cp++ = mode & S_IWUSR ? 'w' : '-';
386   *cp++ = (mode & S_ISUID
387                ? (mode & S_IXUSR ? 's' : 'S')
388                : (mode & S_IXUSR ? 'x' : '-'));
389   *cp++ = mode & S_IRGRP ? 'r' : '-';
390   *cp++ = mode & S_IWGRP ? 'w' : '-';
391   *cp++ = (mode & S_ISGID
392                ? (mode & S_IXGRP ? 's' : 'S')
393                : (mode & S_IXGRP ? 'x' : '-'));
394   *cp++ = mode & S_IROTH ? 'r' : '-';
395   *cp++ = mode & S_IWOTH ? 'w' : '-';
396   *cp++ = (mode & S_ISVTX
397                ? (mode & S_IXOTH ? 't' : 'T')
398                : (mode & S_IXOTH ? 'x' : '-'));
399   *cp = '\0';
400   return cp;
401 }
402
403 #if defined(HAVE_WIN32)
404 int do_shell_expansion(char *name, int name_len)
405 {
406    char *src = bstrdup(name);
407
408    ExpandEnvironmentStrings(src, name, name_len);
409
410    free(src);
411
412    return 1;
413 }
414 #else
415 int do_shell_expansion(char *name, int name_len)
416 {
417    static char meta[] = "~\\$[]*?`'<>\"";
418    bool found = false;
419    int len, i, stat;
420    POOLMEM *cmd;
421    BPIPE *bpipe;
422    char line[MAXSTRING];
423    const char *shellcmd;
424
425    /* Check if any meta characters are present */
426    len = strlen(meta);
427    for (i = 0; i < len; i++) {
428       if (strchr(name, meta[i])) {
429          found = true;
430          break;
431       }
432    }
433    if (found) {
434       cmd =  get_pool_memory(PM_FNAME);
435       /* look for shell */
436       if ((shellcmd = getenv("SHELL")) == NULL) {
437          shellcmd = "/bin/sh";
438       }
439       pm_strcpy(&cmd, shellcmd);
440       pm_strcat(&cmd, " -c \"echo ");
441       pm_strcat(&cmd, name);
442       pm_strcat(&cmd, "\"");
443       Dmsg1(400, "Send: %s\n", cmd);
444       if ((bpipe = open_bpipe(cmd, 0, "r"))) {
445          *line = 0;
446          fgets(line, sizeof(line), bpipe->rfd);
447          strip_trailing_junk(line);
448          stat = close_bpipe(bpipe);
449          Dmsg2(400, "stat=%d got: %s\n", stat, line);
450       } else {
451          stat = 1;                    /* error */
452       }
453       free_pool_memory(cmd);
454       if (stat == 0) {
455          bstrncpy(name, line, name_len);
456       }
457    }
458    return 1;
459 }
460 #endif
461
462
463 /*  MAKESESSIONKEY  --  Generate session key with optional start
464                         key.  If mode is TRUE, the key will be
465                         translated to a string, otherwise it is
466                         returned as 16 binary bytes.
467
468     from SpeakFreely by John Walker */
469
470 void make_session_key(char *key, char *seed, int mode)
471 {
472    int j, k;
473    struct MD5Context md5c;
474    unsigned char md5key[16], md5key1[16];
475    char s[1024];
476
477 #define ss sizeof(s)
478
479    s[0] = 0;
480    if (seed != NULL) {
481      bstrncat(s, seed, sizeof(s));
482    }
483
484    /* The following creates a seed for the session key generator
485      based on a collection of volatile and environment-specific
486      information unlikely to be vulnerable (as a whole) to an
487      exhaustive search attack.  If one of these items isn't
488      available on your machine, replace it with something
489      equivalent or, if you like, just delete it. */
490
491 #if defined(HAVE_WIN32)
492    {
493       LARGE_INTEGER     li;
494       DWORD             length;
495       FILETIME          ft;
496       char             *p;
497
498       p = s;
499       sprintf(s + strlen(s), "%lu", (uint32_t)GetCurrentProcessId());
500       (void)getcwd(s + strlen(s), 256);
501       bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)GetTickCount());
502       QueryPerformanceCounter(&li);
503       bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)li.LowPart);
504       GetSystemTimeAsFileTime(&ft);
505       bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)ft.dwLowDateTime);
506       bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)ft.dwHighDateTime);
507       length = 256;
508       GetComputerName(s + strlen(s), &length);
509       length = 256;
510       GetUserName(s + strlen(s), &length);
511    }
512 #else
513    bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getpid());
514    bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getppid());
515    (void)getcwd(s + strlen(s), 256);
516    bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)clock());
517    bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)time(NULL));
518 #if defined(Solaris)
519    sysinfo(SI_HW_SERIAL,s + strlen(s), 12);
520 #endif
521 #if defined(HAVE_GETHOSTID)
522    bsnprintf(s + strlen(s), ss, "%lu", (uint32_t) gethostid());
523 #endif
524    gethostname(s + strlen(s), 256);
525    bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getuid());
526    bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getgid());
527 #endif
528    MD5Init(&md5c);
529    MD5Update(&md5c, (uint8_t *)s, strlen(s));
530    MD5Final(md5key, &md5c);
531    bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)((time(NULL) + 65121) ^ 0x375F));
532    MD5Init(&md5c);
533    MD5Update(&md5c, (uint8_t *)s, strlen(s));
534    MD5Final(md5key1, &md5c);
535 #define nextrand    (md5key[j] ^ md5key1[j])
536    if (mode) {
537      for (j = k = 0; j < 16; j++) {
538         unsigned char rb = nextrand;
539
540 #define Rad16(x) ((x) + 'A')
541         key[k++] = Rad16((rb >> 4) & 0xF);
542         key[k++] = Rad16(rb & 0xF);
543 #undef Rad16
544         if (j & 1) {
545            key[k++] = '-';
546         }
547      }
548      key[--k] = 0;
549    } else {
550      for (j = 0; j < 16; j++) {
551         key[j] = nextrand;
552      }
553    }
554 }
555 #undef nextrand
556
557 void encode_session_key(char *encode, char *session, char *key, int maxlen)
558 {
559    int i;
560    for (i=0; (i < maxlen-1) && session[i]; i++) {
561       if (session[i] == '-') {
562          encode[i] = '-';
563       } else {
564          encode[i] = ((session[i] - 'A' + key[i]) & 0xF) + 'A';
565       }
566    }
567    encode[i] = 0;
568    Dmsg3(000, "Session=%s key=%s encode=%s\n", session, key, encode);
569 }
570
571 void decode_session_key(char *decode, char *session, char *key, int maxlen)
572 {
573    int i, x;
574
575    for (i=0; (i < maxlen-1) && session[i]; i++) {
576       if (session[i] == '-') {
577          decode[i] = '-';
578       } else {
579          x = (session[i] - 'A' - key[i]) & 0xF;
580          if (x < 0) {
581             x += 16;
582          }
583          decode[i] = x + 'A';
584       }
585    }
586    decode[i] = 0;
587    Dmsg3(000, "Session=%s key=%s decode=%s\n", session, key, decode);
588 }
589
590
591
592 /*
593  * Edit job codes into main command line
594  *  %% = %
595  *  %c = Client's name
596  *  %d = Director's name
597  *  %e = Job Exit code
598  *  %i = JobId
599  *  %j = Unique Job id
600  *  %l = job level
601  *  %n = Unadorned Job name
602  *  %s = Since time
603  *  %t = Job type (Backup, ...)
604  *  %r = Recipients
605  *  %v = Volume name
606  *
607  *  omsg = edited output message
608  *  imsg = input string containing edit codes (%x)
609  *  to = recepients list
610  *
611  */
612 POOLMEM *edit_job_codes(JCR *jcr, char *omsg, char *imsg, const char *to)
613 {
614    char *p, *q;
615    const char *str;
616    char add[20];
617    char name[MAX_NAME_LENGTH];
618    int i;
619
620    *omsg = 0;
621    Dmsg1(200, "edit_job_codes: %s\n", imsg);
622    for (p=imsg; *p; p++) {
623       if (*p == '%') {
624          switch (*++p) {
625          case '%':
626             str = "%";
627             break;
628          case 'c':
629             if (jcr) {
630                str = jcr->client_name;
631             } else {
632                str = _("*none*");
633             }
634             break;
635          case 'd':
636             str = my_name;            /* Director's name */
637             break;
638          case 'e':
639             if (jcr) {
640                str = job_status_to_str(jcr->JobStatus);
641             } else {
642                str = _("*none*");
643             }
644             break;
645          case 'i':
646             if (jcr) {
647                bsnprintf(add, sizeof(add), "%d", jcr->JobId);
648                str = add;
649             } else {
650                str = _("*none*");
651             }
652             break;
653          case 'j':                    /* Job name */
654             if (jcr) {
655                str = jcr->Job;
656             } else {
657                str = _("*none*");
658             }
659             break;
660          case 'l':
661             if (jcr) {
662                str = job_level_to_str(jcr->JobLevel);
663             } else {
664                str = _("*none*");
665             }
666             break;
667          case 'n':
668              if (jcr) {
669                 bstrncpy(name, jcr->Job, sizeof(name));
670                 /* There are three periods after the Job name */
671                 for (i=0; i<3; i++) {
672                    if ((q=strrchr(name, '.')) != NULL) {
673                        *q = 0;
674                    }
675                 }
676                 str = name;
677              } else {
678                 str = _("*none*");
679              }
680              break;
681          case 'r':
682             str = to;
683             break;
684          case 's':                    /* since time */
685             if (jcr && jcr->stime) {
686                str = jcr->stime;
687             } else {
688                str = _("*none*");
689             }
690             break;
691          case 't':
692             if (jcr) {
693                str = job_type_to_str(jcr->JobType);
694             } else {
695                str = _("*none*");
696             }
697             break;
698          case 'v':
699             if (jcr) {
700                if (jcr->VolumeName && jcr->VolumeName[0]) {
701                   str = jcr->VolumeName;
702                } else {
703                   str = "";
704                }
705             } else {
706                str = _("*none*");
707             }
708             break;
709          default:
710             add[0] = '%';
711             add[1] = *p;
712             add[2] = 0;
713             str = add;
714             break;
715          }
716       } else {
717          add[0] = *p;
718          add[1] = 0;
719          str = add;
720       }
721       Dmsg1(1200, "add_str %s\n", str);
722       pm_strcat(&omsg, str);
723       Dmsg1(1200, "omsg=%s\n", omsg);
724    }
725    return omsg;
726 }
727
728 void set_working_directory(char *wd)
729 {
730    struct stat stat_buf;
731
732    if (wd == NULL) {
733       Emsg0(M_ERROR_TERM, 0, _("Working directory not defined. Cannot continue.\n"));
734    }
735    if (stat(wd, &stat_buf) != 0) {
736       Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" not found. Cannot continue.\n"),
737          wd);
738    }
739    if (!S_ISDIR(stat_buf.st_mode)) {
740       Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" is not a directory. Cannot continue.\n"),
741          wd);
742    }
743    working_directory = wd;            /* set global */
744 }
745
746 const char *last_path_separator(const char *str)
747 {
748    if (*str != '\0') {
749       for (const char *p = &str[strlen(str) - 1]; p >= str; p--) {
750          if (IsPathSeparator(*p)) {
751             return p;
752          }
753       }
754    }
755    return NULL;
756 }