2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from many
7 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 Bacula® is a registered trademark of Kern Sibbald.
17 * util.c miscellaneous utility subroutines for Bacula
26 #include "findlib/find.h"
29 * Various Bacula Utility subroutines
33 /* Return true of buffer has all zero bytes */
34 bool is_buf_zero(char *buf, int len)
38 int i, len64, done, rem;
44 /* Optimize by checking uint64_t for zero */
45 len64 = len / sizeof(uint64_t);
46 for (i=0; i < len64; i++) {
51 done = len64 * sizeof(uint64_t); /* bytes already checked */
54 for (i = 0; i < rem; i++) {
63 /* Convert a string in place to lower case */
67 if (B_ISUPPER(*str)) {
68 *str = tolower((int)(*str));
74 /* Convert spaces to non-space character.
75 * This makes scanf of fields containing spaces easier.
78 bash_spaces(char *str)
87 /* Convert spaces to non-space character.
88 * This makes scanf of fields containing spaces easier.
91 bash_spaces(POOL_MEM &pm)
93 char *str = pm.c_str();
102 /* Convert non-space characters (0x1) back into spaces */
104 unbash_spaces(char *str)
113 /* Convert non-space characters (0x1) back into spaces */
115 unbash_spaces(POOL_MEM &pm)
117 char *str = pm.c_str();
125 char *encode_time(utime_t utime, char *buf)
131 #if defined(HAVE_WIN32)
133 * Avoid a seg fault in Microsoft's CRT localtime_r(),
134 * which incorrectly references a NULL returned from gmtime() if
135 * time is negative before or after the timezone adjustment.
139 if ((gtm = gmtime(&time)) == NULL) {
143 if (gtm->tm_year == 1970 && gtm->tm_mon == 1 && gtm->tm_mday < 3) {
148 if (localtime_r(&time, &tm)) {
149 n = sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d",
150 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
151 tm.tm_hour, tm.tm_min, tm.tm_sec);
159 * Convert a JobStatus code into a human readable form
161 void jobstatus_to_ascii(int JobStatus, char *msg, int maxlen)
168 jobstat = _("Created");
171 jobstat = _("Running");
174 jobstat = _("Blocked");
180 case JS_ErrorTerminated:
181 jobstat = _("Error");
184 jobstat = _("Non-fatal error");
187 jobstat = _("OK -- with warnings");
190 jobstat = _("Canceled");
193 jobstat = _("Verify differences");
196 jobstat = _("Waiting on FD");
199 jobstat = _("Wait on SD");
202 jobstat = _("Wait for new Volume");
205 jobstat = _("Waiting for mount");
207 case JS_WaitStoreRes:
208 jobstat = _("Waiting for Storage resource");
211 jobstat = _("Waiting for Job resource");
213 case JS_WaitClientRes:
214 jobstat = _("Waiting for Client resource");
217 jobstat = _("Waiting on Max Jobs");
219 case JS_WaitStartTime:
220 jobstat = _("Waiting for Start Time");
222 case JS_WaitPriority:
223 jobstat = _("Waiting on Priority");
225 case JS_DataCommitting:
226 jobstat = _("SD committing Data");
228 case JS_DataDespooling:
229 jobstat = _("SD despooling Data");
231 case JS_AttrDespooling:
232 jobstat = _("SD despooling Attributes");
234 case JS_AttrInserting:
235 jobstat = _("Dir inserting Attributes");
239 if (JobStatus == 0) {
242 bsnprintf(buf, sizeof(buf), _("Unknown Job termination status=%d"), JobStatus);
247 bstrncpy(msg, jobstat, maxlen);
251 * Convert a JobStatus code into a human readable form - gui version
253 void jobstatus_to_ascii_gui(int JobStatus, char *msg, int maxlen)
255 const char *cnv = NULL;
258 cnv = _("Completed successfully");
261 cnv = _("Completed with warnings");
263 case JS_ErrorTerminated:
264 cnv = _("Terminated with errors");
267 cnv = _("Fatal error");
270 cnv = _("Created, not yet running");
273 cnv = _("Canceled by user");
276 cnv = _("Verify found differences");
279 cnv = _("Waiting for File daemon");
282 cnv = _("Waiting for Storage daemon");
284 case JS_WaitPriority:
285 cnv = _("Waiting for higher priority jobs");
287 case JS_AttrInserting:
288 cnv = _("Batch inserting file records");
293 bstrncpy(msg, cnv, maxlen);
295 jobstatus_to_ascii(JobStatus, msg, maxlen);
301 * Convert Job Termination Status into a string
303 const char *job_status_to_str(int stat)
312 str = _("OK -- with warnings");
314 case JS_ErrorTerminated:
319 str = _("Fatal Error");
325 str = _("Differences");
328 str = _("Unknown term code");
336 * Convert Job Type into a string
338 const char *job_type_to_str(int type)
340 const char *str = NULL;
346 case JT_MIGRATED_JOB:
347 str = _("Migrated Job");
359 str = _("System or Console");
381 str = _("Unknown Type");
386 /* Convert ActionOnPurge to string (Truncate, Erase, Destroy)
388 char *action_on_purge_to_string(int aop, POOL_MEM &ret)
390 if (aop & ON_PURGE_TRUNCATE) {
391 pm_strcpy(ret, _("Truncate"));
394 pm_strcpy(ret, _("None"));
400 * Convert Job Level into a string
402 const char *job_level_to_str(int level)
414 str = _("Incremental");
417 str = _("Differential");
422 case L_VERIFY_CATALOG:
423 str = _("Verify Catalog");
426 str = _("Verify Init Catalog");
428 case L_VERIFY_VOLUME_TO_CATALOG:
429 str = _("Verify Volume to Catalog");
431 case L_VERIFY_DISK_TO_CATALOG:
432 str = _("Verify Disk to Catalog");
435 str = _("Verify Data");
438 str = _("Virtual Full");
444 str = _("Unknown Job Level");
450 const char *volume_status_to_str(const char *status)
454 NT_("Append"), _("Append"),
455 NT_("Archive"), _("Archive"),
456 NT_("Disabled"), _("Disabled"),
457 NT_("Full"), _("Full"),
458 NT_("Used"), _("Used"),
459 NT_("Cleaning"), _("Cleaning"),
460 NT_("Purged"), _("Purged"),
461 NT_("Recycle"), _("Recycle"),
462 NT_("Read-Only"), _("Read-Only"),
463 NT_("Error"), _("Error"),
467 for (pos = 0 ; vs[pos] ; pos += 2) {
468 if ( !strcmp(vs[pos],status) ) {
474 return _("Invalid volume status");
478 /***********************************************************************
479 * Encode the mode bits into a 10 character string like LS does
480 ***********************************************************************/
482 char *encode_mode(mode_t mode, char *buf)
486 *cp++ = S_ISDIR(mode) ? 'd' : S_ISBLK(mode) ? 'b' : S_ISCHR(mode) ? 'c' :
487 S_ISLNK(mode) ? 'l' : S_ISFIFO(mode) ? 'f' : S_ISSOCK(mode) ? 's' : '-';
488 *cp++ = mode & S_IRUSR ? 'r' : '-';
489 *cp++ = mode & S_IWUSR ? 'w' : '-';
490 *cp++ = (mode & S_ISUID
491 ? (mode & S_IXUSR ? 's' : 'S')
492 : (mode & S_IXUSR ? 'x' : '-'));
493 *cp++ = mode & S_IRGRP ? 'r' : '-';
494 *cp++ = mode & S_IWGRP ? 'w' : '-';
495 *cp++ = (mode & S_ISGID
496 ? (mode & S_IXGRP ? 's' : 'S')
497 : (mode & S_IXGRP ? 'x' : '-'));
498 *cp++ = mode & S_IROTH ? 'r' : '-';
499 *cp++ = mode & S_IWOTH ? 'w' : '-';
500 *cp++ = (mode & S_ISVTX
501 ? (mode & S_IXOTH ? 't' : 'T')
502 : (mode & S_IXOTH ? 'x' : '-'));
507 #if defined(HAVE_WIN32)
508 int do_shell_expansion(char *name, int name_len)
510 char *src = bstrdup(name);
512 ExpandEnvironmentStrings(src, name, name_len);
519 int do_shell_expansion(char *name, int name_len)
521 static char meta[] = "~\\$[]*?`'<>\"";
526 char line[MAXSTRING];
527 const char *shellcmd;
529 /* Check if any meta characters are present */
531 for (i = 0; i < len; i++) {
532 if (strchr(name, meta[i])) {
538 cmd = get_pool_memory(PM_FNAME);
540 if ((shellcmd = getenv("SHELL")) == NULL) {
541 shellcmd = "/bin/sh";
543 pm_strcpy(&cmd, shellcmd);
544 pm_strcat(&cmd, " -c \"echo ");
545 pm_strcat(&cmd, name);
546 pm_strcat(&cmd, "\"");
547 Dmsg1(400, "Send: %s\n", cmd);
548 if ((bpipe = open_bpipe(cmd, 0, "r"))) {
550 fgets(line, sizeof(line), bpipe->rfd);
551 strip_trailing_junk(line);
552 stat = close_bpipe(bpipe);
553 Dmsg2(400, "stat=%d got: %s\n", stat, line);
555 stat = 1; /* error */
557 free_pool_memory(cmd);
559 bstrncpy(name, line, name_len);
567 /* MAKESESSIONKEY -- Generate session key with optional start
568 key. If mode is TRUE, the key will be
569 translated to a string, otherwise it is
570 returned as 16 binary bytes.
572 from SpeakFreely by John Walker */
574 void make_session_key(char *key, char *seed, int mode)
577 struct MD5Context md5c;
578 unsigned char md5key[16], md5key1[16];
585 bstrncat(s, seed, sizeof(s));
588 /* The following creates a seed for the session key generator
589 based on a collection of volatile and environment-specific
590 information unlikely to be vulnerable (as a whole) to an
591 exhaustive search attack. If one of these items isn't
592 available on your machine, replace it with something
593 equivalent or, if you like, just delete it. */
595 #if defined(HAVE_WIN32)
601 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)GetCurrentProcessId());
602 (void)getcwd(s + strlen(s), 256);
603 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)GetTickCount());
604 QueryPerformanceCounter(&li);
605 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)li.LowPart);
606 GetSystemTimeAsFileTime(&ft);
607 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)ft.dwLowDateTime);
608 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)ft.dwHighDateTime);
610 GetComputerName(s + strlen(s), &length);
612 GetUserName(s + strlen(s), &length);
615 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getpid());
616 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getppid());
617 (void)getcwd(s + strlen(s), 256);
618 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)clock());
619 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)time(NULL));
621 sysinfo(SI_HW_SERIAL,s + strlen(s), 12);
623 #if defined(HAVE_GETHOSTID)
624 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t) gethostid());
626 gethostname(s + strlen(s), 256);
627 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getuid());
628 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getgid());
631 MD5Update(&md5c, (uint8_t *)s, strlen(s));
632 MD5Final(md5key, &md5c);
633 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)((time(NULL) + 65121) ^ 0x375F));
635 MD5Update(&md5c, (uint8_t *)s, strlen(s));
636 MD5Final(md5key1, &md5c);
637 #define nextrand (md5key[j] ^ md5key1[j])
639 for (j = k = 0; j < 16; j++) {
640 unsigned char rb = nextrand;
642 #define Rad16(x) ((x) + 'A')
643 key[k++] = Rad16((rb >> 4) & 0xF);
644 key[k++] = Rad16(rb & 0xF);
652 for (j = 0; j < 16; j++) {
659 void encode_session_key(char *encode, char *session, char *key, int maxlen)
662 for (i=0; (i < maxlen-1) && session[i]; i++) {
663 if (session[i] == '-') {
666 encode[i] = ((session[i] - 'A' + key[i]) & 0xF) + 'A';
670 Dmsg3(000, "Session=%s key=%s encode=%s\n", session, key, encode);
673 void decode_session_key(char *decode, char *session, char *key, int maxlen)
677 for (i=0; (i < maxlen-1) && session[i]; i++) {
678 if (session[i] == '-') {
681 x = (session[i] - 'A' - key[i]) & 0xF;
689 Dmsg3(000, "Session=%s key=%s decode=%s\n", session, key, decode);
695 * Edit job codes into main command line
698 * %d = Director's name
703 * %n = Unadorned Job name
705 * %t = Job type (Backup, ...)
711 * omsg = edited output message
712 * imsg = input string containing edit codes (%x)
713 * to = recepients list
716 POOLMEM *edit_job_codes(JCR *jcr, char *omsg, char *imsg, const char *to, job_code_callback_t callback)
721 char name[MAX_NAME_LENGTH];
725 Dmsg1(200, "edit_job_codes: %s\n", imsg);
726 for (p=imsg; *p; p++) {
734 str = jcr->client_name;
740 str = my_name; /* Director's name */
744 str = job_status_to_str(jcr->JobStatus);
751 bsnprintf(add, sizeof(add), "%d", jcr->JobId);
757 case 'j': /* Job name */
766 str = job_level_to_str(jcr->getJobLevel());
773 bstrncpy(name, jcr->Job, sizeof(name));
774 /* There are three periods after the Job name */
775 for (i=0; i<3; i++) {
776 if ((q=strrchr(name, '.')) != NULL) {
788 case 's': /* since time */
789 if (jcr && jcr->stime) {
795 case 'F': /* Job Files */
796 str = edit_uint64(jcr->JobFiles, add);
798 case 'b': /* Job Bytes */
799 str = edit_uint64(jcr->JobBytes, add);
803 str = job_type_to_str(jcr->getJobType());
810 if (jcr->VolumeName && jcr->VolumeName[0]) {
811 str = jcr->VolumeName;
820 edit_uint64(getpid(), add);
825 if (callback != NULL) {
826 str = callback(jcr, p);
842 Dmsg1(1200, "add_str %s\n", str);
843 pm_strcat(&omsg, str);
844 Dmsg1(1200, "omsg=%s\n", omsg);
849 void set_working_directory(char *wd)
851 struct stat stat_buf;
854 Emsg0(M_ERROR_TERM, 0, _("Working directory not defined. Cannot continue.\n"));
856 if (stat(wd, &stat_buf) != 0) {
857 Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" not found. Cannot continue.\n"),
860 if (!S_ISDIR(stat_buf.st_mode)) {
861 Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" is not a directory. Cannot continue.\n"),
864 working_directory = wd; /* set global */
867 const char *last_path_separator(const char *str)
870 for (const char *p = &str[strlen(str) - 1]; p >= str; p--) {
871 if (IsPathSeparator(*p)) {