]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/util.c
Add new SD despooling attributes and Dir inserting attributes
[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    s[0] = 0;
478    if (seed != NULL) {
479      bstrncat(s, seed, sizeof(s));
480    }
481
482    /* The following creates a seed for the session key generator
483      based on a collection of volatile and environment-specific
484      information unlikely to be vulnerable (as a whole) to an
485      exhaustive search attack.  If one of these items isn't
486      available on your machine, replace it with something
487      equivalent or, if you like, just delete it. */
488
489 #if defined(HAVE_WIN32)
490    {
491       LARGE_INTEGER     li;
492       DWORD             length;
493       FILETIME          ft;
494       char             *p;
495
496       p = s;
497       sprintf(s + strlen(s), "%lu", (unsigned long)GetCurrentProcessId());
498       (void)getcwd(s + strlen(s), 256);
499       sprintf(s + strlen(s), "%lu", (unsigned long)GetTickCount());
500       QueryPerformanceCounter(&li);
501       sprintf(s + strlen(s), "%lu", (unsigned long)li.LowPart);
502       GetSystemTimeAsFileTime(&ft);
503       sprintf(s + strlen(s), "%lu", (unsigned long)ft.dwLowDateTime);
504       sprintf(s + strlen(s), "%lu", (unsigned long)ft.dwHighDateTime);
505       length = 256;
506       GetComputerName(s + strlen(s), &length);
507       length = 256;
508       GetUserName(s + strlen(s), &length);
509    }
510 #else
511    sprintf(s + strlen(s), "%lu", (unsigned long)getpid());
512    sprintf(s + strlen(s), "%lu", (unsigned long)getppid());
513    (void)getcwd(s + strlen(s), 256);
514    sprintf(s + strlen(s), "%lu", (unsigned long)clock());
515    sprintf(s + strlen(s), "%lu", (unsigned long)time(NULL));
516 #if defined(Solaris)
517    sysinfo(SI_HW_SERIAL,s + strlen(s), 12);
518 #endif
519 #if defined(HAVE_GETHOSTID)
520    sprintf(s + strlen(s), "%lu", (unsigned long) gethostid());
521 #endif
522    gethostname(s + strlen(s), 256);
523    sprintf(s + strlen(s), "%u", (unsigned)getuid());
524    sprintf(s + strlen(s), "%u", (unsigned)getgid());
525 #endif
526    MD5Init(&md5c);
527    MD5Update(&md5c, (unsigned char *)s, strlen(s));
528    MD5Final(md5key, &md5c);
529    sprintf(s + strlen(s), "%lu", (unsigned long)((time(NULL) + 65121) ^ 0x375F));
530    MD5Init(&md5c);
531    MD5Update(&md5c, (unsigned char *)s, strlen(s));
532    MD5Final(md5key1, &md5c);
533 #define nextrand    (md5key[j] ^ md5key1[j])
534    if (mode) {
535      for (j = k = 0; j < 16; j++) {
536         unsigned char rb = nextrand;
537
538 #define Rad16(x) ((x) + 'A')
539         key[k++] = Rad16((rb >> 4) & 0xF);
540         key[k++] = Rad16(rb & 0xF);
541 #undef Rad16
542         if (j & 1) {
543            key[k++] = '-';
544         }
545      }
546      key[--k] = 0;
547    } else {
548      for (j = 0; j < 16; j++) {
549         key[j] = nextrand;
550      }
551    }
552 }
553 #undef nextrand
554
555
556
557 /*
558  * Edit job codes into main command line
559  *  %% = %
560  *  %c = Client's name
561  *  %d = Director's name
562  *  %e = Job Exit code
563  *  %i = JobId
564  *  %j = Unique Job id
565  *  %l = job level
566  *  %n = Unadorned Job name
567  *  %s = Since time
568  *  %t = Job type (Backup, ...)
569  *  %r = Recipients
570  *  %v = Volume name
571  *
572  *  omsg = edited output message
573  *  imsg = input string containing edit codes (%x)
574  *  to = recepients list
575  *
576  */
577 POOLMEM *edit_job_codes(JCR *jcr, char *omsg, char *imsg, const char *to)
578 {
579    char *p, *q;
580    const char *str;
581    char add[20];
582    char name[MAX_NAME_LENGTH];
583    int i;
584
585    *omsg = 0;
586    Dmsg1(200, "edit_job_codes: %s\n", imsg);
587    for (p=imsg; *p; p++) {
588       if (*p == '%') {
589          switch (*++p) {
590          case '%':
591             str = "%";
592             break;
593          case 'c':
594             if (jcr) {
595                str = jcr->client_name;
596             } else {
597                str = _("*none*");
598             }
599             break;
600          case 'd':
601             str = my_name;            /* Director's name */
602             break;
603          case 'e':
604             if (jcr) {
605                str = job_status_to_str(jcr->JobStatus);
606             } else {
607                str = _("*none*");
608             }
609             break;
610          case 'i':
611             if (jcr) {
612                bsnprintf(add, sizeof(add), "%d", jcr->JobId);
613                str = add;
614             } else {
615                str = _("*none*");
616             }
617             break;
618          case 'j':                    /* Job name */
619             if (jcr) {
620                str = jcr->Job;
621             } else {
622                str = _("*none*");
623             }
624             break;
625          case 'l':
626             if (jcr) {
627                str = job_level_to_str(jcr->JobLevel);
628             } else {
629                str = _("*none*");
630             }
631             break;
632          case 'n':
633              if (jcr) {
634                 bstrncpy(name, jcr->Job, sizeof(name));
635                 /* There are three periods after the Job name */
636                 for (i=0; i<3; i++) {
637                    if ((q=strrchr(name, '.')) != NULL) {
638                        *q = 0;
639                    }
640                 }
641                 str = name;
642              } else {
643                 str = _("*none*");
644              }
645              break;
646          case 'r':
647             str = to;
648             break;
649          case 's':                    /* since time */
650             if (jcr && jcr->stime) {
651                str = jcr->stime;
652             } else {
653                str = _("*none*");
654             }
655             break;
656          case 't':
657             if (jcr) {
658                str = job_type_to_str(jcr->JobType);
659             } else {
660                str = _("*none*");
661             }
662             break;
663          case 'v':
664             if (jcr) {
665                if (jcr->VolumeName && jcr->VolumeName[0]) {
666                   str = jcr->VolumeName;
667                } else {
668                   str = "";
669                }
670             } else {
671                str = _("*none*");
672             }
673             break;
674          default:
675             add[0] = '%';
676             add[1] = *p;
677             add[2] = 0;
678             str = add;
679             break;
680          }
681       } else {
682          add[0] = *p;
683          add[1] = 0;
684          str = add;
685       }
686       Dmsg1(1200, "add_str %s\n", str);
687       pm_strcat(&omsg, str);
688       Dmsg1(1200, "omsg=%s\n", omsg);
689    }
690    return omsg;
691 }
692
693 void set_working_directory(char *wd)
694 {
695    struct stat stat_buf;
696
697    if (wd == NULL) {
698       Emsg0(M_ERROR_TERM, 0, _("Working directory not defined. Cannot continue.\n"));
699    }
700    if (stat(wd, &stat_buf) != 0) {
701       Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" not found. Cannot continue.\n"),
702          wd);
703    }
704    if (!S_ISDIR(stat_buf.st_mode)) {
705       Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" is not a directory. Cannot continue.\n"),
706          wd);
707    }
708    working_directory = wd;            /* set global */
709 }
710
711 const char *last_path_separator(const char *str)
712 {
713    if (*str != '\0') {
714       for (const char *p = &str[strlen(str) - 1]; p >= str; p--) {
715          if (IsPathSeparator(*p)) {
716             return p;
717          }
718       }
719    }
720    return NULL;
721 }