2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2008 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");
233 case JS_DataCommitting:
234 jobstat = _("SD committing Data");
236 case JS_DataDespooling:
237 jobstat = _("SD despooling Data");
239 case JS_AttrDespooling:
240 jobstat = _("SD despooling Attributes");
242 case JS_AttrInserting:
243 jobstat = _("Dir inserting Attributes");
247 if (JobStatus == 0) {
250 bsnprintf(buf, sizeof(buf), _("Unknown Job termination status=%d"), JobStatus);
255 bstrncpy(msg, jobstat, maxlen);
259 * Convert Job Termination Status into a string
261 const char *job_status_to_str(int stat)
269 case JS_ErrorTerminated:
274 str = _("Fatal Error");
280 str = _("Differences");
283 str = _("Unknown term code");
291 * Convert Job Type into a string
293 const char *job_type_to_str(int type)
320 str = _("System or Console");
326 str = _("Unknown Type");
333 * Convert Job Level into a string
335 const char *job_level_to_str(int level)
346 str = _("Incremental");
349 str = _("Differential");
354 case L_VERIFY_CATALOG:
355 str = _("Verify Catalog");
358 str = _("Verify Init Catalog");
360 case L_VERIFY_VOLUME_TO_CATALOG:
361 str = _("Verify Volume to Catalog");
363 case L_VERIFY_DISK_TO_CATALOG:
364 str = _("Verify Disk to Catalog");
367 str = _("Verify Data");
373 str = _("Unknown Job Level");
380 /***********************************************************************
381 * Encode the mode bits into a 10 character string like LS does
382 ***********************************************************************/
384 char *encode_mode(mode_t mode, char *buf)
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' : '-'));
409 #if defined(HAVE_WIN32)
410 int do_shell_expansion(char *name, int name_len)
412 char *src = bstrdup(name);
414 ExpandEnvironmentStrings(src, name, name_len);
421 int do_shell_expansion(char *name, int name_len)
423 static char meta[] = "~\\$[]*?`'<>\"";
428 char line[MAXSTRING];
429 const char *shellcmd;
431 /* Check if any meta characters are present */
433 for (i = 0; i < len; i++) {
434 if (strchr(name, meta[i])) {
440 cmd = get_pool_memory(PM_FNAME);
442 if ((shellcmd = getenv("SHELL")) == NULL) {
443 shellcmd = "/bin/sh";
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"))) {
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);
457 stat = 1; /* error */
459 free_pool_memory(cmd);
461 bstrncpy(name, line, name_len);
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.
474 from SpeakFreely by John Walker */
476 void make_session_key(char *key, char *seed, int mode)
479 struct MD5Context md5c;
480 unsigned char md5key[16], md5key1[16];
487 bstrncat(s, seed, sizeof(s));
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. */
497 #if defined(HAVE_WIN32)
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);
514 GetComputerName(s + strlen(s), &length);
516 GetUserName(s + strlen(s), &length);
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));
525 sysinfo(SI_HW_SERIAL,s + strlen(s), 12);
527 #if defined(HAVE_GETHOSTID)
528 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t) gethostid());
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());
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));
539 MD5Update(&md5c, (uint8_t *)s, strlen(s));
540 MD5Final(md5key1, &md5c);
541 #define nextrand (md5key[j] ^ md5key1[j])
543 for (j = k = 0; j < 16; j++) {
544 unsigned char rb = nextrand;
546 #define Rad16(x) ((x) + 'A')
547 key[k++] = Rad16((rb >> 4) & 0xF);
548 key[k++] = Rad16(rb & 0xF);
556 for (j = 0; j < 16; j++) {
563 void encode_session_key(char *encode, char *session, char *key, int maxlen)
566 for (i=0; (i < maxlen-1) && session[i]; i++) {
567 if (session[i] == '-') {
570 encode[i] = ((session[i] - 'A' + key[i]) & 0xF) + 'A';
574 Dmsg3(000, "Session=%s key=%s encode=%s\n", session, key, encode);
577 void decode_session_key(char *decode, char *session, char *key, int maxlen)
581 for (i=0; (i < maxlen-1) && session[i]; i++) {
582 if (session[i] == '-') {
585 x = (session[i] - 'A' - key[i]) & 0xF;
593 Dmsg3(000, "Session=%s key=%s decode=%s\n", session, key, decode);
599 * Edit job codes into main command line
602 * %d = Director's name
607 * %n = Unadorned Job name
609 * %t = Job type (Backup, ...)
613 * omsg = edited output message
614 * imsg = input string containing edit codes (%x)
615 * to = recepients list
618 POOLMEM *edit_job_codes(JCR *jcr, char *omsg, char *imsg, const char *to, job_code_callback_t callback)
623 char name[MAX_NAME_LENGTH];
627 Dmsg1(200, "edit_job_codes: %s\n", imsg);
628 for (p=imsg; *p; p++) {
636 str = jcr->client_name;
642 str = my_name; /* Director's name */
646 str = job_status_to_str(jcr->JobStatus);
653 bsnprintf(add, sizeof(add), "%d", jcr->JobId);
659 case 'j': /* Job name */
668 str = job_level_to_str(jcr->JobLevel);
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) {
690 case 's': /* since time */
691 if (jcr && jcr->stime) {
699 str = job_type_to_str(jcr->JobType);
706 if (jcr->VolumeName && jcr->VolumeName[0]) {
707 str = jcr->VolumeName;
717 if (callback != NULL) {
718 str = callback(jcr, p);
734 Dmsg1(1200, "add_str %s\n", str);
735 pm_strcat(&omsg, str);
736 Dmsg1(1200, "omsg=%s\n", omsg);
741 void set_working_directory(char *wd)
743 struct stat stat_buf;
746 Emsg0(M_ERROR_TERM, 0, _("Working directory not defined. Cannot continue.\n"));
748 if (stat(wd, &stat_buf) != 0) {
749 Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" not found. Cannot continue.\n"),
752 if (!S_ISDIR(stat_buf.st_mode)) {
753 Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" is not a directory. Cannot continue.\n"),
756 working_directory = wd; /* set global */
759 const char *last_path_separator(const char *str)
762 for (const char *p = &str[strlen(str) - 1]; p >= str; p--) {
763 if (IsPathSeparator(*p)) {