2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2009 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)
428 str = _("Incremental");
431 str = _("Differential");
436 case L_VERIFY_CATALOG:
437 str = _("Verify Catalog");
440 str = _("Verify Init Catalog");
442 case L_VERIFY_VOLUME_TO_CATALOG:
443 str = _("Verify Volume to Catalog");
445 case L_VERIFY_DISK_TO_CATALOG:
446 str = _("Verify Disk to Catalog");
449 str = _("Verify Data");
452 str = _("Virtual Full");
458 str = _("Unknown Job Level");
464 const char *volume_status_to_str(const char *status)
468 NT_("Append"), _("Append"),
469 NT_("Archive"), _("Archive"),
470 NT_("Disabled"), _("Disabled"),
471 NT_("Full"), _("Full"),
472 NT_("Used"), _("Used"),
473 NT_("Cleaning"), _("Cleaning"),
474 NT_("Purged"), _("Purged"),
475 NT_("Recycle"), _("Recycle"),
476 NT_("Read-Only"), _("Read-Only"),
477 NT_("Error"), _("Error"),
481 for (pos = 0 ; vs[pos] ; pos += 2) {
482 if ( !strcmp(vs[pos],status) ) {
488 return _("Invalid volume status");
492 /***********************************************************************
493 * Encode the mode bits into a 10 character string like LS does
494 ***********************************************************************/
496 char *encode_mode(mode_t mode, char *buf)
500 *cp++ = S_ISDIR(mode) ? 'd' : S_ISBLK(mode) ? 'b' : S_ISCHR(mode) ? 'c' :
501 S_ISLNK(mode) ? 'l' : S_ISFIFO(mode) ? 'f' : S_ISSOCK(mode) ? 's' : '-';
502 *cp++ = mode & S_IRUSR ? 'r' : '-';
503 *cp++ = mode & S_IWUSR ? 'w' : '-';
504 *cp++ = (mode & S_ISUID
505 ? (mode & S_IXUSR ? 's' : 'S')
506 : (mode & S_IXUSR ? 'x' : '-'));
507 *cp++ = mode & S_IRGRP ? 'r' : '-';
508 *cp++ = mode & S_IWGRP ? 'w' : '-';
509 *cp++ = (mode & S_ISGID
510 ? (mode & S_IXGRP ? 's' : 'S')
511 : (mode & S_IXGRP ? 'x' : '-'));
512 *cp++ = mode & S_IROTH ? 'r' : '-';
513 *cp++ = mode & S_IWOTH ? 'w' : '-';
514 *cp++ = (mode & S_ISVTX
515 ? (mode & S_IXOTH ? 't' : 'T')
516 : (mode & S_IXOTH ? 'x' : '-'));
521 #if defined(HAVE_WIN32)
522 int do_shell_expansion(char *name, int name_len)
524 char *src = bstrdup(name);
526 ExpandEnvironmentStrings(src, name, name_len);
533 int do_shell_expansion(char *name, int name_len)
535 static char meta[] = "~\\$[]*?`'<>\"";
540 char line[MAXSTRING];
541 const char *shellcmd;
543 /* Check if any meta characters are present */
545 for (i = 0; i < len; i++) {
546 if (strchr(name, meta[i])) {
552 cmd = get_pool_memory(PM_FNAME);
554 if ((shellcmd = getenv("SHELL")) == NULL) {
555 shellcmd = "/bin/sh";
557 pm_strcpy(&cmd, shellcmd);
558 pm_strcat(&cmd, " -c \"echo ");
559 pm_strcat(&cmd, name);
560 pm_strcat(&cmd, "\"");
561 Dmsg1(400, "Send: %s\n", cmd);
562 if ((bpipe = open_bpipe(cmd, 0, "r"))) {
564 fgets(line, sizeof(line), bpipe->rfd);
565 strip_trailing_junk(line);
566 stat = close_bpipe(bpipe);
567 Dmsg2(400, "stat=%d got: %s\n", stat, line);
569 stat = 1; /* error */
571 free_pool_memory(cmd);
573 bstrncpy(name, line, name_len);
581 /* MAKESESSIONKEY -- Generate session key with optional start
582 key. If mode is TRUE, the key will be
583 translated to a string, otherwise it is
584 returned as 16 binary bytes.
586 from SpeakFreely by John Walker */
588 void make_session_key(char *key, char *seed, int mode)
591 struct MD5Context md5c;
592 unsigned char md5key[16], md5key1[16];
599 bstrncat(s, seed, sizeof(s));
602 /* The following creates a seed for the session key generator
603 based on a collection of volatile and environment-specific
604 information unlikely to be vulnerable (as a whole) to an
605 exhaustive search attack. If one of these items isn't
606 available on your machine, replace it with something
607 equivalent or, if you like, just delete it. */
609 #if defined(HAVE_WIN32)
617 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)GetCurrentProcessId());
618 (void)getcwd(s + strlen(s), 256);
619 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)GetTickCount());
620 QueryPerformanceCounter(&li);
621 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)li.LowPart);
622 GetSystemTimeAsFileTime(&ft);
623 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)ft.dwLowDateTime);
624 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)ft.dwHighDateTime);
626 GetComputerName(s + strlen(s), &length);
628 GetUserName(s + strlen(s), &length);
631 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getpid());
632 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getppid());
633 (void)getcwd(s + strlen(s), 256);
634 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)clock());
635 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)time(NULL));
637 sysinfo(SI_HW_SERIAL,s + strlen(s), 12);
639 #if defined(HAVE_GETHOSTID)
640 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t) gethostid());
642 gethostname(s + strlen(s), 256);
643 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getuid());
644 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getgid());
647 MD5Update(&md5c, (uint8_t *)s, strlen(s));
648 MD5Final(md5key, &md5c);
649 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)((time(NULL) + 65121) ^ 0x375F));
651 MD5Update(&md5c, (uint8_t *)s, strlen(s));
652 MD5Final(md5key1, &md5c);
653 #define nextrand (md5key[j] ^ md5key1[j])
655 for (j = k = 0; j < 16; j++) {
656 unsigned char rb = nextrand;
658 #define Rad16(x) ((x) + 'A')
659 key[k++] = Rad16((rb >> 4) & 0xF);
660 key[k++] = Rad16(rb & 0xF);
668 for (j = 0; j < 16; j++) {
675 void encode_session_key(char *encode, char *session, char *key, int maxlen)
678 for (i=0; (i < maxlen-1) && session[i]; i++) {
679 if (session[i] == '-') {
682 encode[i] = ((session[i] - 'A' + key[i]) & 0xF) + 'A';
686 Dmsg3(000, "Session=%s key=%s encode=%s\n", session, key, encode);
689 void decode_session_key(char *decode, char *session, char *key, int maxlen)
693 for (i=0; (i < maxlen-1) && session[i]; i++) {
694 if (session[i] == '-') {
697 x = (session[i] - 'A' - key[i]) & 0xF;
705 Dmsg3(000, "Session=%s key=%s decode=%s\n", session, key, decode);
711 * Edit job codes into main command line
714 * %d = Director's name
719 * %n = Unadorned Job name
721 * %t = Job type (Backup, ...)
727 * omsg = edited output message
728 * imsg = input string containing edit codes (%x)
729 * to = recepients list
732 POOLMEM *edit_job_codes(JCR *jcr, char *omsg, char *imsg, const char *to, job_code_callback_t callback)
737 char name[MAX_NAME_LENGTH];
741 Dmsg1(200, "edit_job_codes: %s\n", imsg);
742 for (p=imsg; *p; p++) {
750 str = jcr->client_name;
756 str = my_name; /* Director's name */
760 str = job_status_to_str(jcr->JobStatus);
767 bsnprintf(add, sizeof(add), "%d", jcr->JobId);
773 case 'j': /* Job name */
782 str = job_level_to_str(jcr->getJobLevel());
789 bstrncpy(name, jcr->Job, sizeof(name));
790 /* There are three periods after the Job name */
791 for (i=0; i<3; i++) {
792 if ((q=strrchr(name, '.')) != NULL) {
804 case 's': /* since time */
805 if (jcr && jcr->stime) {
811 case 'f': /* Job Files */
812 str = edit_uint64(jcr->JobFiles, add);
814 case 'b': /* Job Bytes */
815 str = edit_uint64(jcr->JobBytes, add);
819 str = job_type_to_str(jcr->getJobType());
826 if (jcr->VolumeName && jcr->VolumeName[0]) {
827 str = jcr->VolumeName;
837 if (callback != NULL) {
838 str = callback(jcr, p);
854 Dmsg1(1200, "add_str %s\n", str);
855 pm_strcat(&omsg, str);
856 Dmsg1(1200, "omsg=%s\n", omsg);
861 void set_working_directory(char *wd)
863 struct stat stat_buf;
866 Emsg0(M_ERROR_TERM, 0, _("Working directory not defined. Cannot continue.\n"));
868 if (stat(wd, &stat_buf) != 0) {
869 Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" not found. Cannot continue.\n"),
872 if (!S_ISDIR(stat_buf.st_mode)) {
873 Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" is not a directory. Cannot continue.\n"),
876 working_directory = wd; /* set global */
879 const char *last_path_separator(const char *str)
882 for (const char *p = &str[strlen(str) - 1]; p >= str; p--) {
883 if (IsPathSeparator(*p)) {