]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/util.c
kes Enable data encryption code.
[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    Bacula® - The Network Backup Solution
10
11    Copyright (C) 2000-2006 Free Software Foundation Europe e.V.
12
13    The main author of Bacula is Kern Sibbald, with contributions from
14    many others, a complete list can be found in the file AUTHORS.
15    This program is Free Software; you can redistribute it and/or
16    modify it under the terms of version two of the GNU General Public
17    License as published by the Free Software Foundation plus additions
18    that are listed in the file LICENSE.
19
20    This program is distributed in the hope that it will be useful, but
21    WITHOUT ANY WARRANTY; without even the implied warranty of
22    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23    General Public License for more details.
24
25    You should have received a copy of the GNU General Public License
26    along with this program; if not, write to the Free Software
27    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
28    02110-1301, USA.
29
30    Bacula® is a registered trademark of John Walker.
31    The licensor of Bacula is the Free Software Foundation Europe
32    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
33    Switzerland, email:ftf@fsfeurope.org.
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
234    default:
235       if (JobStatus == 0) {
236          buf[0] = 0;
237       } else {
238          bsnprintf(buf, sizeof(buf), _("Unknown Job termination status=%d"), JobStatus);
239       }
240       jobstat = buf;
241       break;
242    }
243    bstrncpy(msg, jobstat, maxlen);
244 }
245
246 /*
247  * Convert Job Termination Status into a string
248  */
249 const char *job_status_to_str(int stat)
250 {
251    const char *str;
252
253    switch (stat) {
254    case JS_Terminated:
255       str = _("OK");
256       break;
257    case JS_ErrorTerminated:
258    case JS_Error:
259       str = _("Error");
260       break;
261    case JS_FatalError:
262       str = _("Fatal Error");
263       break;
264    case JS_Canceled:
265       str = _("Canceled");
266       break;
267    case JS_Differences:
268       str = _("Differences");
269       break;
270    default:
271       str = _("Unknown term code");
272       break;
273    }
274    return str;
275 }
276
277
278 /*
279  * Convert Job Type into a string
280  */
281 const char *job_type_to_str(int type)
282 {
283    const char *str;
284
285    switch (type) {
286    case JT_BACKUP:
287       str = _("Backup");
288       break;
289    case JT_VERIFY:
290       str = _("Verify");
291       break;
292    case JT_RESTORE:
293       str = _("Restore");
294       break;
295    case JT_ADMIN:
296       str = _("Admin");
297       break;
298    case JT_MIGRATE:
299       str = _("Migrate");
300       break;
301    case JT_COPY:
302       str = _("Copy");
303       break;
304    case JT_CONSOLE:
305       str = _("Console");
306       break;
307    case JT_SYSTEM:
308       str = _("System or Console");
309       break;
310    case JT_SCAN:
311       str = _("Scan");
312       break;
313    default:
314       str = _("Unknown Type");
315       break;
316    }
317    return str;
318 }
319
320 /*
321  * Convert Job Level into a string
322  */
323 const char *job_level_to_str(int level)
324 {
325    const char *str;
326
327    switch (level) {
328    case L_BASE:
329       str = _("Base");
330    case L_FULL:
331       str = _("Full");
332       break;
333    case L_INCREMENTAL:
334       str = _("Incremental");
335       break;
336    case L_DIFFERENTIAL:
337       str = _("Differential");
338       break;
339    case L_SINCE:
340       str = _("Since");
341       break;
342    case L_VERIFY_CATALOG:
343       str = _("Verify Catalog");
344       break;
345    case L_VERIFY_INIT:
346       str = _("Verify Init Catalog");
347       break;
348    case L_VERIFY_VOLUME_TO_CATALOG:
349       str = _("Verify Volume to Catalog");
350       break;
351    case L_VERIFY_DISK_TO_CATALOG:
352       str = _("Verify Disk to Catalog");
353       break;
354    case L_VERIFY_DATA:
355       str = _("Verify Data");
356       break;
357    case L_NONE:
358       str = " ";
359       break;
360    default:
361       str = _("Unknown Job Level");
362       break;
363    }
364    return str;
365 }
366
367
368 /***********************************************************************
369  * Encode the mode bits into a 10 character string like LS does
370  ***********************************************************************/
371
372 char *encode_mode(mode_t mode, char *buf)
373 {
374   char *cp = buf;
375
376   *cp++ = S_ISDIR(mode) ? 'd' : S_ISBLK(mode)  ? 'b' : S_ISCHR(mode)  ? 'c' :
377           S_ISLNK(mode) ? 'l' : S_ISFIFO(mode) ? 'f' : S_ISSOCK(mode) ? 's' : '-';
378   *cp++ = mode & S_IRUSR ? 'r' : '-';
379   *cp++ = mode & S_IWUSR ? 'w' : '-';
380   *cp++ = (mode & S_ISUID
381                ? (mode & S_IXUSR ? 's' : 'S')
382                : (mode & S_IXUSR ? 'x' : '-'));
383   *cp++ = mode & S_IRGRP ? 'r' : '-';
384   *cp++ = mode & S_IWGRP ? 'w' : '-';
385   *cp++ = (mode & S_ISGID
386                ? (mode & S_IXGRP ? 's' : 'S')
387                : (mode & S_IXGRP ? 'x' : '-'));
388   *cp++ = mode & S_IROTH ? 'r' : '-';
389   *cp++ = mode & S_IWOTH ? 'w' : '-';
390   *cp++ = (mode & S_ISVTX
391                ? (mode & S_IXOTH ? 't' : 'T')
392                : (mode & S_IXOTH ? 'x' : '-'));
393   *cp = '\0';
394   return cp;
395 }
396
397 #if defined(HAVE_WIN32)
398 int do_shell_expansion(char *name, int name_len)
399 {
400    char *src = bstrdup(name);
401
402    ExpandEnvironmentStrings(src, name, name_len);
403
404    free(src);
405
406    return 1;
407 }
408 #else
409 int do_shell_expansion(char *name, int name_len)
410 {
411    static char meta[] = "~\\$[]*?`'<>\"";
412    bool found = false;
413    int len, i, stat;
414    POOLMEM *cmd;
415    BPIPE *bpipe;
416    char line[MAXSTRING];
417    const char *shellcmd;
418
419    /* Check if any meta characters are present */
420    len = strlen(meta);
421    for (i = 0; i < len; i++) {
422       if (strchr(name, meta[i])) {
423          found = true;
424          break;
425       }
426    }
427    if (found) {
428       cmd =  get_pool_memory(PM_FNAME);
429       /* look for shell */
430       if ((shellcmd = getenv("SHELL")) == NULL) {
431          shellcmd = "/bin/sh";
432       }
433       pm_strcpy(&cmd, shellcmd);
434       pm_strcat(&cmd, " -c \"echo ");
435       pm_strcat(&cmd, name);
436       pm_strcat(&cmd, "\"");
437       Dmsg1(400, "Send: %s\n", cmd);
438       if ((bpipe = open_bpipe(cmd, 0, "r"))) {
439          *line = 0;
440          fgets(line, sizeof(line), bpipe->rfd);
441          strip_trailing_junk(line);
442          stat = close_bpipe(bpipe);
443          Dmsg2(400, "stat=%d got: %s\n", stat, line);
444       } else {
445          stat = 1;                    /* error */
446       }
447       free_pool_memory(cmd);
448       if (stat == 0) {
449          bstrncpy(name, line, name_len);
450       }
451    }
452    return 1;
453 }
454 #endif
455
456
457 /*  MAKESESSIONKEY  --  Generate session key with optional start
458                         key.  If mode is TRUE, the key will be
459                         translated to a string, otherwise it is
460                         returned as 16 binary bytes.
461
462     from SpeakFreely by John Walker */
463
464 void make_session_key(char *key, char *seed, int mode)
465 {
466    int j, k;
467    struct MD5Context md5c;
468    unsigned char md5key[16], md5key1[16];
469    char s[1024];
470
471    s[0] = 0;
472    if (seed != NULL) {
473      bstrncat(s, seed, sizeof(s));
474    }
475
476    /* The following creates a seed for the session key generator
477      based on a collection of volatile and environment-specific
478      information unlikely to be vulnerable (as a whole) to an
479      exhaustive search attack.  If one of these items isn't
480      available on your machine, replace it with something
481      equivalent or, if you like, just delete it. */
482
483 #if defined(HAVE_WIN32)
484    {
485       LARGE_INTEGER     li;
486       DWORD             length;
487       FILETIME          ft;
488       char             *p;
489
490       p = s;
491       sprintf(s + strlen(s), "%lu", (unsigned long)GetCurrentProcessId());
492       (void)getcwd(s + strlen(s), 256);
493       sprintf(s + strlen(s), "%lu", (unsigned long)GetTickCount());
494       QueryPerformanceCounter(&li);
495       sprintf(s + strlen(s), "%lu", (unsigned long)li.LowPart);
496       GetSystemTimeAsFileTime(&ft);
497       sprintf(s + strlen(s), "%lu", (unsigned long)ft.dwLowDateTime);
498       sprintf(s + strlen(s), "%lu", (unsigned long)ft.dwHighDateTime);
499       length = 256;
500       GetComputerName(s + strlen(s), &length);
501       length = 256;
502       GetUserName(s + strlen(s), &length);
503    }
504 #else
505    sprintf(s + strlen(s), "%lu", (unsigned long)getpid());
506    sprintf(s + strlen(s), "%lu", (unsigned long)getppid());
507    (void)getcwd(s + strlen(s), 256);
508    sprintf(s + strlen(s), "%lu", (unsigned long)clock());
509    sprintf(s + strlen(s), "%lu", (unsigned long)time(NULL));
510 #if defined(Solaris)
511    sysinfo(SI_HW_SERIAL,s + strlen(s), 12);
512 #endif
513 #if defined(HAVE_GETHOSTID)
514    sprintf(s + strlen(s), "%lu", (unsigned long) gethostid());
515 #endif
516    gethostname(s + strlen(s), 256);
517    sprintf(s + strlen(s), "%u", (unsigned)getuid());
518    sprintf(s + strlen(s), "%u", (unsigned)getgid());
519 #endif
520    MD5Init(&md5c);
521    MD5Update(&md5c, (unsigned char *)s, strlen(s));
522    MD5Final(md5key, &md5c);
523    sprintf(s + strlen(s), "%lu", (unsigned long)((time(NULL) + 65121) ^ 0x375F));
524    MD5Init(&md5c);
525    MD5Update(&md5c, (unsigned char *)s, strlen(s));
526    MD5Final(md5key1, &md5c);
527 #define nextrand    (md5key[j] ^ md5key1[j])
528    if (mode) {
529      for (j = k = 0; j < 16; j++) {
530         unsigned char rb = nextrand;
531
532 #define Rad16(x) ((x) + 'A')
533         key[k++] = Rad16((rb >> 4) & 0xF);
534         key[k++] = Rad16(rb & 0xF);
535 #undef Rad16
536         if (j & 1) {
537            key[k++] = '-';
538         }
539      }
540      key[--k] = 0;
541    } else {
542      for (j = 0; j < 16; j++) {
543         key[j] = nextrand;
544      }
545    }
546 }
547 #undef nextrand
548
549
550
551 /*
552  * Edit job codes into main command line
553  *  %% = %
554  *  %c = Client's name
555  *  %d = Director's name
556  *  %e = Job Exit code
557  *  %i = JobId
558  *  %j = Unique Job id
559  *  %l = job level
560  *  %n = Unadorned Job name
561  *  %s = Since time
562  *  %t = Job type (Backup, ...)
563  *  %r = Recipients
564  *  %v = Volume name
565  *
566  *  omsg = edited output message
567  *  imsg = input string containing edit codes (%x)
568  *  to = recepients list
569  *
570  */
571 POOLMEM *edit_job_codes(JCR *jcr, char *omsg, char *imsg, const char *to)
572 {
573    char *p, *q;
574    const char *str;
575    char add[20];
576    char name[MAX_NAME_LENGTH];
577    int i;
578
579    *omsg = 0;
580    Dmsg1(200, "edit_job_codes: %s\n", imsg);
581    for (p=imsg; *p; p++) {
582       if (*p == '%') {
583          switch (*++p) {
584          case '%':
585             str = "%";
586             break;
587          case 'c':
588             if (jcr) {
589                str = jcr->client_name;
590             } else {
591                str = _("*none*");
592             }
593             break;
594          case 'd':
595             str = my_name;            /* Director's name */
596             break;
597          case 'e':
598             if (jcr) {
599                str = job_status_to_str(jcr->JobStatus);
600             } else {
601                str = _("*none*");
602             }
603             break;
604          case 'i':
605             if (jcr) {
606                bsnprintf(add, sizeof(add), "%d", jcr->JobId);
607                str = add;
608             } else {
609                str = _("*none*");
610             }
611             break;
612          case 'j':                    /* Job name */
613             if (jcr) {
614                str = jcr->Job;
615             } else {
616                str = _("*none*");
617             }
618             break;
619          case 'l':
620             if (jcr) {
621                str = job_level_to_str(jcr->JobLevel);
622             } else {
623                str = _("*none*");
624             }
625             break;
626          case 'n':
627              if (jcr) {
628                 bstrncpy(name, jcr->Job, sizeof(name));
629                 /* There are three periods after the Job name */
630                 for (i=0; i<3; i++) {
631                    if ((q=strrchr(name, '.')) != NULL) {
632                        *q = 0;
633                    }
634                 }
635                 str = name;
636              } else {
637                 str = _("*none*");
638              }
639              break;
640          case 'r':
641             str = to;
642             break;
643          case 's':                    /* since time */
644             if (jcr && jcr->stime) {
645                str = jcr->stime;
646             } else {
647                str = _("*none*");
648             }
649             break;
650          case 't':
651             if (jcr) {
652                str = job_type_to_str(jcr->JobType);
653             } else {
654                str = _("*none*");
655             }
656             break;
657          case 'v':
658             if (jcr) {
659                if (jcr->VolumeName && jcr->VolumeName[0]) {
660                   str = jcr->VolumeName;
661                } else {
662                   str = "";
663                }
664             } else {
665                str = _("*none*");
666             }
667             break;
668          default:
669             add[0] = '%';
670             add[1] = *p;
671             add[2] = 0;
672             str = add;
673             break;
674          }
675       } else {
676          add[0] = *p;
677          add[1] = 0;
678          str = add;
679       }
680       Dmsg1(1200, "add_str %s\n", str);
681       pm_strcat(&omsg, str);
682       Dmsg1(1200, "omsg=%s\n", omsg);
683    }
684    return omsg;
685 }
686
687 void set_working_directory(char *wd)
688 {
689    struct stat stat_buf;
690
691    if (wd == NULL) {
692       Emsg0(M_ERROR_TERM, 0, _("Working directory not defined. Cannot continue.\n"));
693    }
694    if (stat(wd, &stat_buf) != 0) {
695       Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" not found. Cannot continue.\n"),
696          wd);
697    }
698    if (!S_ISDIR(stat_buf.st_mode)) {
699       Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" is not a directory. Cannot continue.\n"),
700          wd);
701    }
702    working_directory = wd;            /* set global */
703 }
704
705 const char *last_path_separator(const char *str)
706 {
707    if (*str != '\0') {
708       for (const char *p = &str[strlen(str) - 1]; p >= str; p--) {
709          if (IsPathSeparator(*p)) {
710             return p;
711          }
712       }
713    }
714    return NULL;
715 }