2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2006 Free Software Foundation Europe e.V.
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
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.
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
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.
29 * util.c miscellaneous utility subroutines for Bacula
38 #include "findlib/find.h"
41 * Various Bacula Utility subroutines
45 /* Return true of buffer has all zero bytes */
46 bool is_buf_zero(char *buf, int len)
50 int i, len64, done, rem;
56 /* Optimize by checking uint64_t for zero */
57 len64 = len / sizeof(uint64_t);
58 for (i=0; i < len64; i++) {
63 done = len64 * sizeof(uint64_t); /* bytes already checked */
66 for (i = 0; i < rem; i++) {
75 /* Convert a string in place to lower case */
79 if (B_ISUPPER(*str)) {
80 *str = tolower((int)(*str));
86 /* Convert spaces to non-space character.
87 * This makes scanf of fields containing spaces easier.
90 bash_spaces(char *str)
99 /* Convert spaces to non-space character.
100 * This makes scanf of fields containing spaces easier.
103 bash_spaces(POOL_MEM &pm)
105 char *str = pm.c_str();
114 /* Convert non-space characters (0x1) back into spaces */
116 unbash_spaces(char *str)
125 /* Convert non-space characters (0x1) back into spaces */
127 unbash_spaces(POOL_MEM &pm)
129 char *str = pm.c_str();
137 char *encode_time(time_t time, char *buf)
142 #if defined(HAVE_WIN32)
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.
150 if ((gtm = gmtime(&time)) == NULL) {
154 if (gtm->tm_year == 1970 && gtm->tm_mon == 1 && gtm->tm_mday < 3) {
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);
170 * Convert a JobStatus code into a human readable form
172 void jobstatus_to_ascii(int JobStatus, char *msg, int maxlen)
179 jobstat = _("Created");
182 jobstat = _("Running");
185 jobstat = _("Blocked");
191 case JS_ErrorTerminated:
192 jobstat = _("Error");
195 jobstat = _("Non-fatal error");
198 jobstat = _("Canceled");
201 jobstat = _("Verify differences");
204 jobstat = _("Waiting on FD");
207 jobstat = _("Wait on SD");
210 jobstat = _("Wait for new Volume");
213 jobstat = _("Waiting for mount");
215 case JS_WaitStoreRes:
216 jobstat = _("Waiting for Storage resource");
219 jobstat = _("Waiting for Job resource");
221 case JS_WaitClientRes:
222 jobstat = _("Waiting for Client resource");
225 jobstat = _("Waiting on Max Jobs");
227 case JS_WaitStartTime:
228 jobstat = _("Waiting for Start Time");
230 case JS_WaitPriority:
231 jobstat = _("Waiting on Priority");
235 if (JobStatus == 0) {
238 bsnprintf(buf, sizeof(buf), _("Unknown Job termination status=%d"), JobStatus);
243 bstrncpy(msg, jobstat, maxlen);
247 * Convert Job Termination Status into a string
249 const char *job_status_to_str(int stat)
257 case JS_ErrorTerminated:
262 str = _("Fatal Error");
268 str = _("Differences");
271 str = _("Unknown term code");
279 * Convert Job Type into a string
281 const char *job_type_to_str(int type)
308 str = _("System or Console");
314 str = _("Unknown Type");
321 * Convert Job Level into a string
323 const char *job_level_to_str(int level)
334 str = _("Incremental");
337 str = _("Differential");
342 case L_VERIFY_CATALOG:
343 str = _("Verify Catalog");
346 str = _("Verify Init Catalog");
348 case L_VERIFY_VOLUME_TO_CATALOG:
349 str = _("Verify Volume to Catalog");
351 case L_VERIFY_DISK_TO_CATALOG:
352 str = _("Verify Disk to Catalog");
355 str = _("Verify Data");
361 str = _("Unknown Job Level");
368 /***********************************************************************
369 * Encode the mode bits into a 10 character string like LS does
370 ***********************************************************************/
372 char *encode_mode(mode_t mode, char *buf)
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' : '-'));
397 #if defined(HAVE_WIN32)
398 int do_shell_expansion(char *name, int name_len)
400 char *src = bstrdup(name);
402 ExpandEnvironmentStrings(src, name, name_len);
409 int do_shell_expansion(char *name, int name_len)
411 static char meta[] = "~\\$[]*?`'<>\"";
416 char line[MAXSTRING];
417 const char *shellcmd;
419 /* Check if any meta characters are present */
421 for (i = 0; i < len; i++) {
422 if (strchr(name, meta[i])) {
428 cmd = get_pool_memory(PM_FNAME);
430 if ((shellcmd = getenv("SHELL")) == NULL) {
431 shellcmd = "/bin/sh";
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"))) {
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);
445 stat = 1; /* error */
447 free_pool_memory(cmd);
449 bstrncpy(name, line, name_len);
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.
462 from SpeakFreely by John Walker */
464 void make_session_key(char *key, char *seed, int mode)
467 struct MD5Context md5c;
468 unsigned char md5key[16], md5key1[16];
473 bstrncat(s, seed, sizeof(s));
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. */
483 #if defined(HAVE_WIN32)
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);
500 GetComputerName(s + strlen(s), &length);
502 GetUserName(s + strlen(s), &length);
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));
511 sysinfo(SI_HW_SERIAL,s + strlen(s), 12);
513 #if defined(HAVE_GETHOSTID)
514 sprintf(s + strlen(s), "%lu", (unsigned long) gethostid());
516 gethostname(s + strlen(s), 256);
517 sprintf(s + strlen(s), "%u", (unsigned)getuid());
518 sprintf(s + strlen(s), "%u", (unsigned)getgid());
521 MD5Update(&md5c, (unsigned char *)s, strlen(s));
522 MD5Final(md5key, &md5c);
523 sprintf(s + strlen(s), "%lu", (unsigned long)((time(NULL) + 65121) ^ 0x375F));
525 MD5Update(&md5c, (unsigned char *)s, strlen(s));
526 MD5Final(md5key1, &md5c);
527 #define nextrand (md5key[j] ^ md5key1[j])
529 for (j = k = 0; j < 16; j++) {
530 unsigned char rb = nextrand;
532 #define Rad16(x) ((x) + 'A')
533 key[k++] = Rad16((rb >> 4) & 0xF);
534 key[k++] = Rad16(rb & 0xF);
542 for (j = 0; j < 16; j++) {
552 * Edit job codes into main command line
555 * %d = Director's name
560 * %n = Unadorned Job name
562 * %t = Job type (Backup, ...)
566 * omsg = edited output message
567 * imsg = input string containing edit codes (%x)
568 * to = recepients list
571 POOLMEM *edit_job_codes(JCR *jcr, char *omsg, char *imsg, const char *to)
576 char name[MAX_NAME_LENGTH];
580 Dmsg1(200, "edit_job_codes: %s\n", imsg);
581 for (p=imsg; *p; p++) {
589 str = jcr->client_name;
595 str = my_name; /* Director's name */
599 str = job_status_to_str(jcr->JobStatus);
606 bsnprintf(add, sizeof(add), "%d", jcr->JobId);
612 case 'j': /* Job name */
621 str = job_level_to_str(jcr->JobLevel);
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) {
643 case 's': /* since time */
644 if (jcr && jcr->stime) {
652 str = job_type_to_str(jcr->JobType);
659 if (jcr->VolumeName && jcr->VolumeName[0]) {
660 str = jcr->VolumeName;
680 Dmsg1(1200, "add_str %s\n", str);
681 pm_strcat(&omsg, str);
682 Dmsg1(1200, "omsg=%s\n", omsg);
687 void set_working_directory(char *wd)
689 struct stat stat_buf;
692 Emsg0(M_ERROR_TERM, 0, _("Working directory not defined. Cannot continue.\n"));
694 if (stat(wd, &stat_buf) != 0) {
695 Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" not found. Cannot continue.\n"),
698 if (!S_ISDIR(stat_buf.st_mode)) {
699 Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" is not a directory. Cannot continue.\n"),
702 working_directory = wd; /* set global */
705 const char *last_path_separator(const char *str)
708 for (const char *p = &str[strlen(str) - 1]; p >= str; p--) {
709 if (IsPathSeparator(*p)) {