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