2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2011 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 three of the GNU Affero 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 Affero 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 Kern Sibbald.
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(utime_t utime, char *buf)
143 #if defined(HAVE_WIN32)
145 * Avoid a seg fault in Microsoft's CRT localtime_r(),
146 * which incorrectly references a NULL returned from gmtime() if
147 * time is negative before or after the timezone adjustment.
151 if ((gtm = gmtime(&time)) == NULL) {
155 if (gtm->tm_year == 1970 && gtm->tm_mon == 1 && gtm->tm_mday < 3) {
160 if (localtime_r(&time, &tm)) {
161 n = sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d",
162 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
163 tm.tm_hour, tm.tm_min, tm.tm_sec);
171 * Convert a JobStatus code into a human readable form
173 void jobstatus_to_ascii(int JobStatus, char *msg, int maxlen)
180 jobstat = _("Created");
183 jobstat = _("Running");
186 jobstat = _("Blocked");
192 jobstat = _("Error: incomplete job");
195 case JS_ErrorTerminated:
196 jobstat = _("Error");
199 jobstat = _("Non-fatal error");
202 jobstat = _("OK -- with warnings");
205 jobstat = _("Canceled");
208 jobstat = _("Verify differences");
211 jobstat = _("Waiting on FD");
214 jobstat = _("Wait on SD");
217 jobstat = _("Wait for new Volume");
220 jobstat = _("Waiting for mount");
222 case JS_WaitStoreRes:
223 jobstat = _("Waiting for Storage resource");
226 jobstat = _("Waiting for Job resource");
228 case JS_WaitClientRes:
229 jobstat = _("Waiting for Client resource");
232 jobstat = _("Waiting on Max Jobs");
234 case JS_WaitStartTime:
235 jobstat = _("Waiting for Start Time");
237 case JS_WaitPriority:
238 jobstat = _("Waiting on Priority");
240 case JS_DataCommitting:
241 jobstat = _("SD committing Data");
243 case JS_DataDespooling:
244 jobstat = _("SD despooling Data");
246 case JS_AttrDespooling:
247 jobstat = _("SD despooling Attributes");
249 case JS_AttrInserting:
250 jobstat = _("Dir inserting Attributes");
254 if (JobStatus == 0) {
257 bsnprintf(buf, sizeof(buf), _("Unknown Job termination status=%d"), JobStatus);
262 bstrncpy(msg, jobstat, maxlen);
266 * Convert a JobStatus code into a human readable form - gui version
268 void jobstatus_to_ascii_gui(int JobStatus, char *msg, int maxlen)
270 const char *cnv = NULL;
273 cnv = _("Completed successfully");
276 cnv = _("Completed with warnings");
278 case JS_ErrorTerminated:
279 cnv = _("Terminated with errors");
282 cnv = _("Fatal error");
285 cnv = _("Created, not yet running");
288 cnv = _("Canceled by user");
291 cnv = _("Verify found differences");
294 cnv = _("Waiting for File daemon");
297 cnv = _("Waiting for Storage daemon");
299 case JS_WaitPriority:
300 cnv = _("Waiting for higher priority jobs");
302 case JS_AttrInserting:
303 cnv = _("Batch inserting file records");
308 bstrncpy(msg, cnv, maxlen);
310 jobstatus_to_ascii(JobStatus, msg, maxlen);
316 * Convert Job Termination Status into a string
318 const char *job_status_to_str(int stat)
327 str = _("OK -- with warnings");
329 case JS_ErrorTerminated:
334 str = _("Fatal Error");
340 str = _("Differences");
343 str = _("Unknown term code");
351 * Convert Job Type into a string
353 const char *job_type_to_str(int type)
355 const char *str = NULL;
361 case JT_MIGRATED_JOB:
362 str = _("Migrated Job");
374 str = _("System or Console");
396 str = _("Unknown Type");
401 /* Convert ActionOnPurge to string (Truncate, Erase, Destroy)
403 char *action_on_purge_to_string(int aop, POOL_MEM &ret)
405 if (aop & ON_PURGE_TRUNCATE) {
406 pm_strcpy(ret, _("Truncate"));
409 pm_strcpy(ret, _("None"));
415 * Convert Job Level into a string
417 const char *job_level_to_str(int level)
429 str = _("Incremental");
432 str = _("Differential");
437 case L_VERIFY_CATALOG:
438 str = _("Verify Catalog");
441 str = _("Verify Init Catalog");
443 case L_VERIFY_VOLUME_TO_CATALOG:
444 str = _("Verify Volume to Catalog");
446 case L_VERIFY_DISK_TO_CATALOG:
447 str = _("Verify Disk to Catalog");
450 str = _("Verify Data");
453 str = _("Virtual Full");
459 str = _("Unknown Job Level");
465 const char *volume_status_to_str(const char *status)
469 NT_("Append"), _("Append"),
470 NT_("Archive"), _("Archive"),
471 NT_("Disabled"), _("Disabled"),
472 NT_("Full"), _("Full"),
473 NT_("Used"), _("Used"),
474 NT_("Cleaning"), _("Cleaning"),
475 NT_("Purged"), _("Purged"),
476 NT_("Recycle"), _("Recycle"),
477 NT_("Read-Only"), _("Read-Only"),
478 NT_("Error"), _("Error"),
482 for (pos = 0 ; vs[pos] ; pos += 2) {
483 if ( !strcmp(vs[pos],status) ) {
489 return _("Invalid volume status");
493 /***********************************************************************
494 * Encode the mode bits into a 10 character string like LS does
495 ***********************************************************************/
497 char *encode_mode(mode_t mode, char *buf)
501 *cp++ = S_ISDIR(mode) ? 'd' : S_ISBLK(mode) ? 'b' : S_ISCHR(mode) ? 'c' :
502 S_ISLNK(mode) ? 'l' : S_ISFIFO(mode) ? 'f' : S_ISSOCK(mode) ? 's' : '-';
503 *cp++ = mode & S_IRUSR ? 'r' : '-';
504 *cp++ = mode & S_IWUSR ? 'w' : '-';
505 *cp++ = (mode & S_ISUID
506 ? (mode & S_IXUSR ? 's' : 'S')
507 : (mode & S_IXUSR ? 'x' : '-'));
508 *cp++ = mode & S_IRGRP ? 'r' : '-';
509 *cp++ = mode & S_IWGRP ? 'w' : '-';
510 *cp++ = (mode & S_ISGID
511 ? (mode & S_IXGRP ? 's' : 'S')
512 : (mode & S_IXGRP ? 'x' : '-'));
513 *cp++ = mode & S_IROTH ? 'r' : '-';
514 *cp++ = mode & S_IWOTH ? 'w' : '-';
515 *cp++ = (mode & S_ISVTX
516 ? (mode & S_IXOTH ? 't' : 'T')
517 : (mode & S_IXOTH ? 'x' : '-'));
522 #if defined(HAVE_WIN32)
523 int do_shell_expansion(char *name, int name_len)
525 char *src = bstrdup(name);
527 ExpandEnvironmentStrings(src, name, name_len);
534 int do_shell_expansion(char *name, int name_len)
536 static char meta[] = "~\\$[]*?`'<>\"";
541 char line[MAXSTRING];
542 const char *shellcmd;
544 /* Check if any meta characters are present */
546 for (i = 0; i < len; i++) {
547 if (strchr(name, meta[i])) {
553 cmd = get_pool_memory(PM_FNAME);
555 if ((shellcmd = getenv("SHELL")) == NULL) {
556 shellcmd = "/bin/sh";
558 pm_strcpy(&cmd, shellcmd);
559 pm_strcat(&cmd, " -c \"echo ");
560 pm_strcat(&cmd, name);
561 pm_strcat(&cmd, "\"");
562 Dmsg1(400, "Send: %s\n", cmd);
563 if ((bpipe = open_bpipe(cmd, 0, "r"))) {
565 fgets(line, sizeof(line), bpipe->rfd);
566 strip_trailing_junk(line);
567 stat = close_bpipe(bpipe);
568 Dmsg2(400, "stat=%d got: %s\n", stat, line);
570 stat = 1; /* error */
572 free_pool_memory(cmd);
574 bstrncpy(name, line, name_len);
582 /* MAKESESSIONKEY -- Generate session key with optional start
583 key. If mode is TRUE, the key will be
584 translated to a string, otherwise it is
585 returned as 16 binary bytes.
587 from SpeakFreely by John Walker */
589 void make_session_key(char *key, char *seed, int mode)
592 struct MD5Context md5c;
593 unsigned char md5key[16], md5key1[16];
600 bstrncat(s, seed, sizeof(s));
603 /* The following creates a seed for the session key generator
604 based on a collection of volatile and environment-specific
605 information unlikely to be vulnerable (as a whole) to an
606 exhaustive search attack. If one of these items isn't
607 available on your machine, replace it with something
608 equivalent or, if you like, just delete it. */
610 #if defined(HAVE_WIN32)
618 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)GetCurrentProcessId());
619 (void)getcwd(s + strlen(s), 256);
620 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)GetTickCount());
621 QueryPerformanceCounter(&li);
622 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)li.LowPart);
623 GetSystemTimeAsFileTime(&ft);
624 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)ft.dwLowDateTime);
625 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)ft.dwHighDateTime);
627 GetComputerName(s + strlen(s), &length);
629 GetUserName(s + strlen(s), &length);
632 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getpid());
633 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getppid());
634 (void)getcwd(s + strlen(s), 256);
635 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)clock());
636 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)time(NULL));
638 sysinfo(SI_HW_SERIAL,s + strlen(s), 12);
640 #if defined(HAVE_GETHOSTID)
641 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t) gethostid());
643 gethostname(s + strlen(s), 256);
644 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getuid());
645 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getgid());
648 MD5Update(&md5c, (uint8_t *)s, strlen(s));
649 MD5Final(md5key, &md5c);
650 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)((time(NULL) + 65121) ^ 0x375F));
652 MD5Update(&md5c, (uint8_t *)s, strlen(s));
653 MD5Final(md5key1, &md5c);
654 #define nextrand (md5key[j] ^ md5key1[j])
656 for (j = k = 0; j < 16; j++) {
657 unsigned char rb = nextrand;
659 #define Rad16(x) ((x) + 'A')
660 key[k++] = Rad16((rb >> 4) & 0xF);
661 key[k++] = Rad16(rb & 0xF);
669 for (j = 0; j < 16; j++) {
676 void encode_session_key(char *encode, char *session, char *key, int maxlen)
679 for (i=0; (i < maxlen-1) && session[i]; i++) {
680 if (session[i] == '-') {
683 encode[i] = ((session[i] - 'A' + key[i]) & 0xF) + 'A';
687 Dmsg3(000, "Session=%s key=%s encode=%s\n", session, key, encode);
690 void decode_session_key(char *decode, char *session, char *key, int maxlen)
694 for (i=0; (i < maxlen-1) && session[i]; i++) {
695 if (session[i] == '-') {
698 x = (session[i] - 'A' - key[i]) & 0xF;
706 Dmsg3(000, "Session=%s key=%s decode=%s\n", session, key, decode);
712 * Edit job codes into main command line
715 * %d = Director's name
720 * %n = Unadorned Job name
722 * %t = Job type (Backup, ...)
728 * omsg = edited output message
729 * imsg = input string containing edit codes (%x)
730 * to = recepients list
733 POOLMEM *edit_job_codes(JCR *jcr, char *omsg, char *imsg, const char *to, job_code_callback_t callback)
738 char name[MAX_NAME_LENGTH];
742 Dmsg1(200, "edit_job_codes: %s\n", imsg);
743 for (p=imsg; *p; p++) {
751 str = jcr->client_name;
757 str = my_name; /* Director's name */
761 str = job_status_to_str(jcr->JobStatus);
768 bsnprintf(add, sizeof(add), "%d", jcr->JobId);
774 case 'j': /* Job name */
783 str = job_level_to_str(jcr->getJobLevel());
790 bstrncpy(name, jcr->Job, sizeof(name));
791 /* There are three periods after the Job name */
792 for (i=0; i<3; i++) {
793 if ((q=strrchr(name, '.')) != NULL) {
805 case 's': /* since time */
806 if (jcr && jcr->stime) {
812 case 'f': /* Job Files */
813 str = edit_uint64(jcr->JobFiles, add);
815 case 'b': /* Job Bytes */
816 str = edit_uint64(jcr->JobBytes, add);
820 str = job_type_to_str(jcr->getJobType());
827 if (jcr->VolumeName && jcr->VolumeName[0]) {
828 str = jcr->VolumeName;
838 if (callback != NULL) {
839 str = callback(jcr, p);
855 Dmsg1(1200, "add_str %s\n", str);
856 pm_strcat(&omsg, str);
857 Dmsg1(1200, "omsg=%s\n", omsg);
862 void set_working_directory(char *wd)
864 struct stat stat_buf;
867 Emsg0(M_ERROR_TERM, 0, _("Working directory not defined. Cannot continue.\n"));
869 if (stat(wd, &stat_buf) != 0) {
870 Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" not found. Cannot continue.\n"),
873 if (!S_ISDIR(stat_buf.st_mode)) {
874 Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" is not a directory. Cannot continue.\n"),
877 working_directory = wd; /* set global */
880 const char *last_path_separator(const char *str)
883 for (const char *p = &str[strlen(str) - 1]; p >= str; p--) {
884 if (IsPathSeparator(*p)) {