2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2016 Kern Sibbald
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
20 * util.c miscellaneous utility subroutines for Bacula
27 #include "findlib/find.h"
30 * Various Bacula Utility subroutines
34 /* Return true of buffer has all zero bytes */
35 bool is_buf_zero(const char *buf, int len)
39 int i, len64, done, rem;
45 /* Optimize by checking uint64_t for zero */
46 len64 = len / sizeof(uint64_t);
47 for (i=0; i < len64; i++) {
52 done = len64 * sizeof(uint64_t); /* bytes already checked */
55 for (i = 0; i < rem; i++) {
64 /* Convert a string in place to lower case */
68 if (B_ISUPPER(*str)) {
69 *str = tolower((int)(*str));
75 /* Convert spaces to non-space character.
76 * This makes scanf of fields containing spaces easier.
79 bash_spaces(char *str)
88 /* Convert spaces to non-space character.
89 * This makes scanf of fields containing spaces easier.
92 bash_spaces(POOL_MEM &pm)
94 char *str = pm.c_str();
103 /* Convert non-space characters (0x1) back into spaces */
105 unbash_spaces(char *str)
114 /* Convert non-space characters (0x1) back into spaces */
116 unbash_spaces(POOL_MEM &pm)
118 char *str = pm.c_str();
126 char *encode_time(utime_t utime, char *buf)
132 #if defined(HAVE_WIN32)
134 * Avoid a seg fault in Microsoft's CRT localtime_r(),
135 * which incorrectly references a NULL returned from gmtime() if
136 * time is negative before or after the timezone adjustment.
140 if ((gtm = gmtime(&time)) == NULL) {
144 if (gtm->tm_year == 1970 && gtm->tm_mon == 1 && gtm->tm_mday < 3) {
149 if (localtime_r(&time, &tm)) {
150 n = sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d",
151 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
152 tm.tm_hour, tm.tm_min, tm.tm_sec);
159 static char hexatable[]="0123456789abcdef";
162 * do an hexadump of data[0:len] into buf[0:capacity]
163 * a space is inserted between every 4 bytes
166 * Dmsg2("msglen=%d msg=%s", fd->msglen, hexdump(fd->msg, fd->msglen, buf, sizeof(buf));
168 * msglen=36 msg=12345678 12345678
170 char *hexdump(const char *data, int len, char *buf, int capacity)
174 while (i<len && capacity>2) {
180 *(b++)=hexatable[(data[i]&0xF0)>>4];
181 *(b++)=hexatable[data[i++]&0x0F];
190 * do an ASCII dump of data[0:len] into buf[0:capacity]
191 * non printable chars are replaced by hexa "\xx"
194 * Dmsg2("msglen=%d msg=%s", fd->msglen, asciidump(fd->msg, fd->msglen, buf, sizeof(buf));
196 * msglen=5 msg=abcd\10
198 char *asciidump(const char *data, int len, char *buf, int capacity)
201 const unsigned char *p=(const unsigned char *)data;
202 while (len>0 && capacity>1) {
209 *(b++)=hexatable[((*p)&0xF0)>>4];
210 *(b++)=hexatable[(*(p++))&0x0F];
220 char *smartdump(const char *data, int len, char *buf, int capacity, bool *is_ascii)
225 const unsigned char *p=(const unsigned char *)data;
226 if (is_ascii != NULL) {
232 } else if (isspace(*p) || *p=='\0') {
236 return hexdump(data, len, buf, capacity);
242 if (is_ascii != NULL) {
249 * Convert a JobStatus code into a human readable form
251 void jobstatus_to_ascii(int JobStatus, char *msg, int maxlen)
258 jobstat = _("Created");
261 jobstat = _("Running");
264 jobstat = _("Blocked");
270 jobstat = _("Incomplete job");
273 case JS_ErrorTerminated:
274 jobstat = _("Error");
277 jobstat = _("Non-fatal error");
280 jobstat = _("OK -- with warnings");
283 jobstat = _("Canceled");
286 jobstat = _("Verify differences");
289 jobstat = _("Waiting on FD");
292 jobstat = _("Wait on SD");
295 jobstat = _("Wait for new Volume");
298 jobstat = _("Waiting for mount");
300 case JS_WaitStoreRes:
301 jobstat = _("Waiting for Storage resource");
304 jobstat = _("Waiting for Job resource");
306 case JS_WaitClientRes:
307 jobstat = _("Waiting for Client resource");
310 jobstat = _("Waiting on Max Jobs");
312 case JS_WaitStartTime:
313 jobstat = _("Waiting for Start Time");
315 case JS_WaitPriority:
316 jobstat = _("Waiting on Priority");
318 case JS_DataCommitting:
319 jobstat = _("SD committing Data");
321 case JS_DataDespooling:
322 jobstat = _("SD despooling Data");
324 case JS_AttrDespooling:
325 jobstat = _("SD despooling Attributes");
327 case JS_AttrInserting:
328 jobstat = _("Dir inserting Attributes");
332 if (JobStatus == 0) {
335 bsnprintf(buf, sizeof(buf), _("Unknown Job termination status=%d"), JobStatus);
340 bstrncpy(msg, jobstat, maxlen);
344 * Convert a JobStatus code into a human readable form - gui version
346 void jobstatus_to_ascii_gui(int JobStatus, char *msg, int maxlen)
348 const char *cnv = NULL;
351 cnv = _("Completed successfully");
354 cnv = _("Completed with warnings");
356 case JS_ErrorTerminated:
357 cnv = _("Terminated with errors");
360 cnv = _("Fatal error");
363 cnv = _("Created, not yet running");
366 cnv = _("Canceled by user");
369 cnv = _("Verify found differences");
372 cnv = _("Waiting for File daemon");
375 cnv = _("Waiting for Storage daemon");
377 case JS_WaitPriority:
378 cnv = _("Waiting for higher priority jobs");
380 case JS_AttrInserting:
381 cnv = _("Batch inserting file records");
386 bstrncpy(msg, cnv, maxlen);
388 jobstatus_to_ascii(JobStatus, msg, maxlen);
393 * Convert Job Termination Status into a string
395 const char *job_status_to_str(int status, int errors)
402 str = _("OK -- with warnings");
408 str = _("OK -- with warnings");
410 case JS_ErrorTerminated:
415 str = _("Fatal Error");
421 str = _("Differences");
424 str = _("Unknown term code");
432 * Convert Job Type into a string
434 const char *job_type_to_str(int type)
436 const char *str = NULL;
442 case JT_MIGRATED_JOB:
443 str = _("Migrated Job");
455 str = _("System or Console");
477 str = _("Unknown Type");
482 /* Convert ActionOnPurge to string (Truncate, Erase, Destroy)
484 char *action_on_purge_to_string(int aop, POOL_MEM &ret)
486 if (aop & ON_PURGE_TRUNCATE) {
487 pm_strcpy(ret, _("Truncate"));
490 pm_strcpy(ret, _("None"));
496 * Convert Job Level into a string
498 const char *job_level_to_str(int level)
510 str = _("Incremental");
513 str = _("Differential");
518 case L_VERIFY_CATALOG:
519 str = _("Verify Catalog");
522 str = _("Verify Init Catalog");
524 case L_VERIFY_VOLUME_TO_CATALOG:
525 str = _("Verify Volume to Catalog");
527 case L_VERIFY_DISK_TO_CATALOG:
528 str = _("Verify Disk to Catalog");
531 str = _("Verify Data");
534 str = _("Virtual Full");
540 str = _("Unknown Job Level");
546 const char *volume_status_to_str(const char *status)
550 NT_("Append"), _("Append"),
551 NT_("Archive"), _("Archive"),
552 NT_("Disabled"), _("Disabled"),
553 NT_("Full"), _("Full"),
554 NT_("Used"), _("Used"),
555 NT_("Cleaning"), _("Cleaning"),
556 NT_("Purged"), _("Purged"),
557 NT_("Recycle"), _("Recycle"),
558 NT_("Read-Only"), _("Read-Only"),
559 NT_("Error"), _("Error"),
563 for (pos = 0 ; vs[pos] ; pos += 2) {
564 if ( !strcmp(vs[pos],status) ) {
570 return _("Invalid volume status");
574 /***********************************************************************
575 * Encode the mode bits into a 10 character string like LS does
576 ***********************************************************************/
578 char *encode_mode(mode_t mode, char *buf)
582 *cp++ = S_ISDIR(mode) ? 'd' : S_ISBLK(mode) ? 'b' : S_ISCHR(mode) ? 'c' :
583 S_ISLNK(mode) ? 'l' : S_ISFIFO(mode) ? 'f' : S_ISSOCK(mode) ? 's' : '-';
584 *cp++ = mode & S_IRUSR ? 'r' : '-';
585 *cp++ = mode & S_IWUSR ? 'w' : '-';
586 *cp++ = (mode & S_ISUID
587 ? (mode & S_IXUSR ? 's' : 'S')
588 : (mode & S_IXUSR ? 'x' : '-'));
589 *cp++ = mode & S_IRGRP ? 'r' : '-';
590 *cp++ = mode & S_IWGRP ? 'w' : '-';
591 *cp++ = (mode & S_ISGID
592 ? (mode & S_IXGRP ? 's' : 'S')
593 : (mode & S_IXGRP ? 'x' : '-'));
594 *cp++ = mode & S_IROTH ? 'r' : '-';
595 *cp++ = mode & S_IWOTH ? 'w' : '-';
596 *cp++ = (mode & S_ISVTX
597 ? (mode & S_IXOTH ? 't' : 'T')
598 : (mode & S_IXOTH ? 'x' : '-'));
603 #if defined(HAVE_WIN32)
604 int do_shell_expansion(char *name, int name_len)
606 char *src = bstrdup(name);
608 ExpandEnvironmentStrings(src, name, name_len);
615 int do_shell_expansion(char *name, int name_len)
617 static char meta[] = "~\\$[]*?`'<>\"";
622 char line[MAXSTRING];
623 const char *shellcmd;
625 /* Check if any meta characters are present */
627 for (i = 0; i < len; i++) {
628 if (strchr(name, meta[i])) {
634 cmd = get_pool_memory(PM_FNAME);
636 if ((shellcmd = getenv("SHELL")) == NULL) {
637 shellcmd = "/bin/sh";
639 pm_strcpy(&cmd, shellcmd);
640 pm_strcat(&cmd, " -c \"echo ");
641 pm_strcat(&cmd, name);
642 pm_strcat(&cmd, "\"");
643 Dmsg1(400, "Send: %s\n", cmd);
644 if ((bpipe = open_bpipe(cmd, 0, "r"))) {
646 fgets(line, sizeof(line), bpipe->rfd);
647 strip_trailing_junk(line);
648 stat = close_bpipe(bpipe);
649 Dmsg2(400, "stat=%d got: %s\n", stat, line);
651 stat = 1; /* error */
653 free_pool_memory(cmd);
655 bstrncpy(name, line, name_len);
663 /* MAKESESSIONKEY -- Generate session key with optional start
664 key. If mode is TRUE, the key will be
665 translated to a string, otherwise it is
666 returned as 16 binary bytes.
668 from SpeakFreely by John Walker */
670 void make_session_key(char *key, char *seed, int mode)
673 struct MD5Context md5c;
674 unsigned char md5key[16], md5key1[16];
681 bstrncat(s, seed, sizeof(s));
684 /* The following creates a seed for the session key generator
685 based on a collection of volatile and environment-specific
686 information unlikely to be vulnerable (as a whole) to an
687 exhaustive search attack. If one of these items isn't
688 available on your machine, replace it with something
689 equivalent or, if you like, just delete it. */
691 #if defined(HAVE_WIN32)
697 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)GetCurrentProcessId());
698 (void)getcwd(s + strlen(s), 256);
699 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)GetTickCount());
700 QueryPerformanceCounter(&li);
701 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)li.LowPart);
702 GetSystemTimeAsFileTime(&ft);
703 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)ft.dwLowDateTime);
704 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)ft.dwHighDateTime);
706 GetComputerName(s + strlen(s), &length);
708 GetUserName(s + strlen(s), &length);
711 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getpid());
712 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getppid());
713 (void)getcwd(s + strlen(s), 256);
714 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)clock());
715 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)time(NULL));
717 sysinfo(SI_HW_SERIAL,s + strlen(s), 12);
719 #if defined(HAVE_GETHOSTID)
720 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t) gethostid());
722 gethostname(s + strlen(s), 256);
723 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getuid());
724 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getgid());
727 MD5Update(&md5c, (uint8_t *)s, strlen(s));
728 MD5Final(md5key, &md5c);
729 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)((time(NULL) + 65121) ^ 0x375F));
731 MD5Update(&md5c, (uint8_t *)s, strlen(s));
732 MD5Final(md5key1, &md5c);
733 #define nextrand (md5key[j] ^ md5key1[j])
735 for (j = k = 0; j < 16; j++) {
736 unsigned char rb = nextrand;
738 #define Rad16(x) ((x) + 'A')
739 key[k++] = Rad16((rb >> 4) & 0xF);
740 key[k++] = Rad16(rb & 0xF);
748 for (j = 0; j < 16; j++) {
755 void encode_session_key(char *encode, char *session, char *key, int maxlen)
758 for (i=0; (i < maxlen-1) && session[i]; i++) {
759 if (session[i] == '-') {
762 encode[i] = ((session[i] - 'A' + key[i]) & 0xF) + 'A';
766 Dmsg3(000, "Session=%s key=%s encode=%s\n", session, key, encode);
769 void decode_session_key(char *decode, char *session, char *key, int maxlen)
773 for (i=0; (i < maxlen-1) && session[i]; i++) {
774 if (session[i] == '-') {
777 x = (session[i] - 'A' - key[i]) & 0xF;
785 Dmsg3(000, "Session=%s key=%s decode=%s\n", session, key, decode);
791 * Edit job codes into main command line
794 * %d = Director's name
799 * %n = Unadorned Job name
801 * %t = Job type (Backup, ...)
809 * omsg = edited output message
810 * imsg = input string containing edit codes (%x)
811 * to = recepients list
814 POOLMEM *edit_job_codes(JCR *jcr, char *omsg, char *imsg, const char *to, job_code_callback_t callback)
819 char name[MAX_NAME_LENGTH];
823 Dmsg1(200, "edit_job_codes: %s\n", imsg);
824 for (p=imsg; *p; p++) {
832 str = jcr->client_name;
838 str = my_name; /* Director's name */
842 str = job_status_to_str(jcr->JobStatus, jcr->getErrors());
847 case 'E': /* Job Errors */
848 str = edit_uint64(jcr->getErrors(), add);
852 bsnprintf(add, sizeof(add), "%d", jcr->JobId);
858 case 'j': /* Job name */
867 str = job_level_to_str(jcr->getJobLevel());
874 bstrncpy(name, jcr->Job, sizeof(name));
875 /* There are three periods after the Job name */
876 for (i=0; i<3; i++) {
877 if ((q=strrchr(name, '.')) != NULL) {
889 case 's': /* since time */
890 if (jcr && jcr->stime) {
896 case 'F': /* Job Files */
897 str = edit_uint64(jcr->JobFiles, add);
899 case 'b': /* Job Bytes */
900 str = edit_uint64(jcr->JobBytes, add);
904 str = job_type_to_str(jcr->getJobType());
911 if (jcr->VolumeName && jcr->VolumeName[0]) {
912 str = jcr->VolumeName;
921 edit_uint64(getpid(), add);
924 case 'R': /* Job ReadBytes */
925 str = edit_uint64(jcr->ReadBytes, add);
929 if (callback != NULL) {
930 str = callback(jcr, p);
946 Dmsg1(1200, "add_str %s\n", str);
947 pm_strcat(&omsg, str);
948 Dmsg1(1200, "omsg=%s\n", omsg);
953 void set_working_directory(char *wd)
955 struct stat stat_buf;
958 Emsg0(M_ERROR_TERM, 0, _("Working directory not defined. Cannot continue.\n"));
960 if (stat(wd, &stat_buf) != 0) {
961 Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" not found. Cannot continue.\n"),
964 if (!S_ISDIR(stat_buf.st_mode)) {
965 Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" is not a directory. Cannot continue.\n"),
968 working_directory = wd; /* set global */
971 const char *last_path_separator(const char *str)
974 for (const char *p = &str[strlen(str) - 1]; p >= str; p--) {
975 if (IsPathSeparator(*p)) {