2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2017 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 bool is_null(const void *ptr)
39 /* Return true of buffer has all zero bytes */
40 bool is_buf_zero(const char *buf, int len)
44 int i, len64, done, rem;
50 /* Optimize by checking uint64_t for zero */
51 len64 = len / sizeof(uint64_t);
52 for (i=0; i < len64; i++) {
57 done = len64 * sizeof(uint64_t); /* bytes already checked */
60 for (i = 0; i < rem; i++) {
69 * Subroutine that cannot be suppressed by GCC 6.0
71 void bmemzero(void *buf, size_t size)
78 /* Convert a string in place to lower case */
82 if (B_ISUPPER(*str)) {
83 *str = tolower((int)(*str));
89 /* Convert spaces to non-space character.
90 * This makes scanf of fields containing spaces easier.
93 bash_spaces(char *str)
102 /* Convert spaces to non-space character.
103 * This makes scanf of fields containing spaces easier.
106 bash_spaces(POOL_MEM &pm)
108 char *str = pm.c_str();
117 /* Convert non-space characters (0x1) back into spaces */
119 unbash_spaces(char *str)
128 /* Convert non-space characters (0x1) back into spaces */
130 unbash_spaces(POOL_MEM &pm)
132 char *str = pm.c_str();
140 char *encode_time(utime_t utime, char *buf)
146 #if defined(HAVE_WIN32)
148 * Avoid a seg fault in Microsoft's CRT localtime_r(),
149 * which incorrectly references a NULL returned from gmtime() if
150 * time is negative before or after the timezone adjustment.
154 if ((gtm = gmtime(&time)) == NULL) {
158 if (gtm->tm_year == 1970 && gtm->tm_mon == 1 && gtm->tm_mday < 3) {
163 if (localtime_r(&time, &tm)) {
164 n = sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d",
165 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
166 tm.tm_hour, tm.tm_min, tm.tm_sec);
173 static char hexatable[]="0123456789abcdef";
176 * do an hexadump of data[0:len] into buf[0:capacity]
177 * a space is inserted between every 4 bytes
180 * Dmsg2("msglen=%d msg=%s", fd->msglen, hexdump(fd->msg, fd->msglen, buf, sizeof(buf));
182 * msglen=36 msg=12345678 12345678
184 char *hexdump(const char *data, int len, char *buf, int capacity, bool add_spaces)
188 while (i<len && capacity>2) {
189 if (add_spaces && i>0 && i%4==0 ) {
194 *(b++)=hexatable[(data[i]&0xF0)>>4];
195 *(b++)=hexatable[data[i++]&0x0F];
204 * do an ASCII dump of data[0:len] into buf[0:capacity]
205 * non printable chars are replaced by hexa "\xx"
208 * Dmsg2("msglen=%d msg=%s", fd->msglen, asciidump(fd->msg, fd->msglen, buf, sizeof(buf));
210 * msglen=5 msg=abcd\10
212 char *asciidump(const char *data, int len, char *buf, int capacity)
215 const unsigned char *p=(const unsigned char *)data;
217 strncpy(buf, "<NULL>", capacity);
220 while (len>0 && capacity>1) {
227 *(b++)=hexatable[((*p)&0xF0)>>4];
228 *(b++)=hexatable[(*(p++))&0x0F];
238 char *smartdump(const char *data, int len, char *buf, int capacity, bool *is_ascii)
243 const unsigned char *p=(const unsigned char *)data;
245 strncpy(buf, "<NULL>", capacity);
248 if (is_ascii != NULL) {
254 } else if (isspace(*p) || *p=='\0') {
258 return hexdump(data, len, buf, capacity);
264 if (is_ascii != NULL) {
271 * check if x is a power two
273 int is_power_of_two(uint64_t x)
275 while ( x%2 == 0 && x > 1) {
282 * Convert a JobStatus code into a human readable form
284 void jobstatus_to_ascii(int JobStatus, char *msg, int maxlen)
291 jobstat = _("Created");
294 jobstat = _("Running");
297 jobstat = _("Blocked");
303 jobstat = _("Incomplete job");
306 case JS_ErrorTerminated:
307 jobstat = _("Error");
310 jobstat = _("Non-fatal error");
313 jobstat = _("OK -- with warnings");
316 jobstat = _("Canceled");
319 jobstat = _("Verify differences");
322 jobstat = _("Waiting on FD");
325 jobstat = _("Wait on SD");
328 jobstat = _("Wait for new Volume");
331 jobstat = _("Waiting for mount");
333 case JS_WaitStoreRes:
334 jobstat = _("Waiting for Storage resource");
337 jobstat = _("Waiting for Job resource");
339 case JS_WaitClientRes:
340 jobstat = _("Waiting for Client resource");
343 jobstat = _("Waiting on Max Jobs");
345 case JS_WaitStartTime:
346 jobstat = _("Waiting for Start Time");
348 case JS_WaitPriority:
349 jobstat = _("Waiting on Priority");
351 case JS_DataCommitting:
352 jobstat = _("SD committing Data");
354 case JS_DataDespooling:
355 jobstat = _("SD despooling Data");
357 case JS_AttrDespooling:
358 jobstat = _("SD despooling Attributes");
360 case JS_AttrInserting:
361 jobstat = _("Dir inserting Attributes");
365 if (JobStatus == 0) {
368 bsnprintf(buf, sizeof(buf), _("Unknown Job termination status=%d"), JobStatus);
373 bstrncpy(msg, jobstat, maxlen);
377 * Convert a JobStatus code into a human readable form - gui version
379 void jobstatus_to_ascii_gui(int JobStatus, char *msg, int maxlen)
381 const char *cnv = NULL;
384 cnv = _("Completed successfully");
387 cnv = _("Completed with warnings");
389 case JS_ErrorTerminated:
390 cnv = _("Terminated with errors");
393 cnv = _("Fatal error");
396 cnv = _("Created, not yet running");
399 cnv = _("Canceled by user");
402 cnv = _("Verify found differences");
405 cnv = _("Waiting for File daemon");
408 cnv = _("Waiting for Storage daemon");
410 case JS_WaitPriority:
411 cnv = _("Waiting for higher priority jobs");
413 case JS_AttrInserting:
414 cnv = _("Batch inserting file records");
419 bstrncpy(msg, cnv, maxlen);
421 jobstatus_to_ascii(JobStatus, msg, maxlen);
426 * Convert Job Termination Status into a string
428 const char *job_status_to_str(int status, int errors)
435 str = _("OK -- with warnings");
441 str = _("OK -- with warnings");
443 case JS_ErrorTerminated:
448 str = _("Fatal Error");
454 str = _("Differences");
460 str = _("Incomplete");
463 str = _("Unknown term code");
471 * Convert Job Type into a string
473 const char *job_type_to_str(int type)
475 const char *str = NULL;
481 case JT_MIGRATED_JOB:
482 str = _("Migrated Job");
494 str = _("System or Console");
516 str = _("Unknown Type");
521 /* Convert ActionOnPurge to string (Truncate, Erase, Destroy)
523 char *action_on_purge_to_string(int aop, POOL_MEM &ret)
525 if (aop & ON_PURGE_TRUNCATE) {
526 pm_strcpy(ret, _("Truncate"));
529 pm_strcpy(ret, _("None"));
535 * Convert Job Level into a string
537 const char *job_level_to_str(int level)
549 str = _("Incremental");
552 str = _("Differential");
557 case L_VERIFY_CATALOG:
558 str = _("Verify Catalog");
561 str = _("Verify Init Catalog");
563 case L_VERIFY_VOLUME_TO_CATALOG:
564 str = _("Verify Volume to Catalog");
566 case L_VERIFY_DISK_TO_CATALOG:
567 str = _("Verify Disk to Catalog");
570 str = _("Verify Data");
573 str = _("Virtual Full");
579 str = _("Unknown Job Level");
585 const char *volume_status_to_str(const char *status)
589 NT_("Append"), _("Append"),
590 NT_("Archive"), _("Archive"),
591 NT_("Disabled"), _("Disabled"),
592 NT_("Full"), _("Full"),
593 NT_("Used"), _("Used"),
594 NT_("Cleaning"), _("Cleaning"),
595 NT_("Purged"), _("Purged"),
596 NT_("Recycle"), _("Recycle"),
597 NT_("Read-Only"), _("Read-Only"),
598 NT_("Error"), _("Error"),
602 for (pos = 0 ; vs[pos] ; pos += 2) {
603 if ( !strcmp(vs[pos],status) ) {
609 return _("Invalid volume status");
613 /***********************************************************************
614 * Encode the mode bits into a 10 character string like LS does
615 ***********************************************************************/
617 char *encode_mode(mode_t mode, char *buf)
621 *cp++ = S_ISDIR(mode) ? 'd' : S_ISBLK(mode) ? 'b' : S_ISCHR(mode) ? 'c' :
622 S_ISLNK(mode) ? 'l' : S_ISFIFO(mode) ? 'f' : S_ISSOCK(mode) ? 's' : '-';
623 *cp++ = mode & S_IRUSR ? 'r' : '-';
624 *cp++ = mode & S_IWUSR ? 'w' : '-';
625 *cp++ = (mode & S_ISUID
626 ? (mode & S_IXUSR ? 's' : 'S')
627 : (mode & S_IXUSR ? 'x' : '-'));
628 *cp++ = mode & S_IRGRP ? 'r' : '-';
629 *cp++ = mode & S_IWGRP ? 'w' : '-';
630 *cp++ = (mode & S_ISGID
631 ? (mode & S_IXGRP ? 's' : 'S')
632 : (mode & S_IXGRP ? 'x' : '-'));
633 *cp++ = mode & S_IROTH ? 'r' : '-';
634 *cp++ = mode & S_IWOTH ? 'w' : '-';
635 *cp++ = (mode & S_ISVTX
636 ? (mode & S_IXOTH ? 't' : 'T')
637 : (mode & S_IXOTH ? 'x' : '-'));
642 #if defined(HAVE_WIN32)
643 int do_shell_expansion(char *name, int name_len)
645 char *src = bstrdup(name);
647 ExpandEnvironmentStrings(src, name, name_len);
654 int do_shell_expansion(char *name, int name_len)
656 static char meta[] = "~\\$[]*?`'<>\"";
661 char line[MAXSTRING];
662 const char *shellcmd;
664 /* Check if any meta characters are present */
666 for (i = 0; i < len; i++) {
667 if (strchr(name, meta[i])) {
673 cmd = get_pool_memory(PM_FNAME);
675 if ((shellcmd = getenv("SHELL")) == NULL) {
676 shellcmd = "/bin/sh";
678 pm_strcpy(&cmd, shellcmd);
679 pm_strcat(&cmd, " -c \"echo ");
680 pm_strcat(&cmd, name);
681 pm_strcat(&cmd, "\"");
682 Dmsg1(400, "Send: %s\n", cmd);
683 if ((bpipe = open_bpipe(cmd, 0, "r"))) {
685 fgets(line, sizeof(line), bpipe->rfd);
686 strip_trailing_junk(line);
687 stat = close_bpipe(bpipe);
688 Dmsg2(400, "stat=%d got: %s\n", stat, line);
690 stat = 1; /* error */
692 free_pool_memory(cmd);
694 bstrncpy(name, line, name_len);
702 /* MAKESESSIONKEY -- Generate session key with optional start
703 key. If mode is TRUE, the key will be
704 translated to a string, otherwise it is
705 returned as 16 binary bytes.
707 from SpeakFreely by John Walker */
709 void make_session_key(char *key, char *seed, int mode)
712 struct MD5Context md5c;
713 unsigned char md5key[16], md5key1[16];
720 bstrncat(s, seed, sizeof(s));
723 /* The following creates a seed for the session key generator
724 based on a collection of volatile and environment-specific
725 information unlikely to be vulnerable (as a whole) to an
726 exhaustive search attack. If one of these items isn't
727 available on your machine, replace it with something
728 equivalent or, if you like, just delete it. */
730 #if defined(HAVE_WIN32)
736 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)GetCurrentProcessId());
737 (void)getcwd(s + strlen(s), 256);
738 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)GetTickCount());
739 QueryPerformanceCounter(&li);
740 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)li.LowPart);
741 GetSystemTimeAsFileTime(&ft);
742 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)ft.dwLowDateTime);
743 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)ft.dwHighDateTime);
745 GetComputerName(s + strlen(s), &length);
747 GetUserName(s + strlen(s), &length);
750 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getpid());
751 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getppid());
752 (void)getcwd(s + strlen(s), 256);
753 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)clock());
754 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)time(NULL));
756 sysinfo(SI_HW_SERIAL,s + strlen(s), 12);
758 #if defined(HAVE_GETHOSTID)
759 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t) gethostid());
761 gethostname(s + strlen(s), 256);
762 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getuid());
763 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getgid());
766 MD5Update(&md5c, (uint8_t *)s, strlen(s));
767 MD5Final(md5key, &md5c);
768 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)((time(NULL) + 65121) ^ 0x375F));
770 MD5Update(&md5c, (uint8_t *)s, strlen(s));
771 MD5Final(md5key1, &md5c);
772 #define nextrand (md5key[j] ^ md5key1[j])
774 for (j = k = 0; j < 16; j++) {
775 unsigned char rb = nextrand;
777 #define Rad16(x) ((x) + 'A')
778 key[k++] = Rad16((rb >> 4) & 0xF);
779 key[k++] = Rad16(rb & 0xF);
787 for (j = 0; j < 16; j++) {
794 void encode_session_key(char *encode, char *session, char *key, int maxlen)
797 for (i=0; (i < maxlen-1) && session[i]; i++) {
798 if (session[i] == '-') {
801 encode[i] = ((session[i] - 'A' + key[i]) & 0xF) + 'A';
805 Dmsg3(000, "Session=%s key=%s encode=%s\n", session, key, encode);
808 void decode_session_key(char *decode, char *session, char *key, int maxlen)
812 for (i=0; (i < maxlen-1) && session[i]; i++) {
813 if (session[i] == '-') {
816 x = (session[i] - 'A' - key[i]) & 0xF;
824 Dmsg3(000, "Session=%s key=%s decode=%s\n", session, key, decode);
830 * Edit job codes into main command line
833 * %d = Director's name
838 * %n = Unadorned Job name
839 * %p = Pool name (Director)
841 * %w = Write Store (Director)
842 * %x = Spool Data (Director)
843 * %D = Director name (Director/FileDaemon)
844 * %C = Cloned (Director)
845 * %I = wjcr->JobId (Director)
846 * %f = FileSet (Director)
847 * %h = Client Address (Director)
849 * %t = Job type (Backup, ...)
856 * %S = Previous Job name (FileDaemon) for Incremental/Differential
858 * omsg = edited output message
859 * imsg = input string containing edit codes (%x)
860 * to = recepients list
863 POOLMEM *edit_job_codes(JCR *jcr, char *omsg, char *imsg, const char *to, job_code_callback_t callback)
868 char name[MAX_NAME_LENGTH];
872 Dmsg1(200, "edit_job_codes: %s\n", imsg);
873 for (p=imsg; *p; p++) {
881 str = jcr->client_name;
887 str = my_name; /* Director's name */
891 str = job_status_to_str(jcr->JobStatus, jcr->getErrors());
896 case 'E': /* Job Errors */
897 str = edit_uint64(jcr->getErrors(), add);
901 bsnprintf(add, sizeof(add), "%d", jcr->JobId);
907 case 'j': /* Job name */
916 str = job_level_to_str(jcr->getJobLevel());
923 bstrncpy(name, jcr->Job, sizeof(name));
924 /* There are three periods after the Job name */
925 for (i=0; i<3; i++) {
926 if ((q=strrchr(name, '.')) != NULL) {
938 case 's': /* since time */
939 if (jcr && jcr->stime) {
945 case 'F': /* Job Files */
946 str = edit_uint64(jcr->JobFiles, add);
948 case 'b': /* Job Bytes */
949 str = edit_uint64(jcr->JobBytes, add);
953 str = job_type_to_str(jcr->getJobType());
960 if (jcr->VolumeName && jcr->VolumeName[0]) {
961 str = jcr->VolumeName;
970 edit_uint64(getpid(), add);
973 case 'R': /* Job ReadBytes */
974 str = edit_uint64(jcr->ReadBytes, add);
978 if (callback != NULL) {
979 str = callback(jcr, p, add, sizeof(add));
995 Dmsg1(1200, "add_str %s\n", str);
996 pm_strcat(&omsg, str);
997 Dmsg1(1200, "omsg=%s\n", omsg);
1002 void set_working_directory(char *wd)
1004 struct stat stat_buf;
1007 Emsg0(M_ERROR_TERM, 0, _("Working directory not defined. Cannot continue.\n"));
1009 if (stat(wd, &stat_buf) != 0) {
1010 Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" not found. Cannot continue.\n"),
1013 if (!S_ISDIR(stat_buf.st_mode)) {
1014 Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" is not a directory. Cannot continue.\n"),
1017 working_directory = wd; /* set global */
1020 const char *last_path_separator(const char *str)
1023 for (const char *p = &str[strlen(str) - 1]; p >= str; p--) {
1024 if (IsPathSeparator(*p)) {