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