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 case JS_ErrorTerminated:
193 jobstat = _("Error");
196 jobstat = _("Non-fatal error");
199 jobstat = _("OK -- with warnings");
202 jobstat = _("Canceled");
205 jobstat = _("Verify differences");
208 jobstat = _("Waiting on FD");
211 jobstat = _("Wait on SD");
214 jobstat = _("Wait for new Volume");
217 jobstat = _("Waiting for mount");
219 case JS_WaitStoreRes:
220 jobstat = _("Waiting for Storage resource");
223 jobstat = _("Waiting for Job resource");
225 case JS_WaitClientRes:
226 jobstat = _("Waiting for Client resource");
229 jobstat = _("Waiting on Max Jobs");
231 case JS_WaitStartTime:
232 jobstat = _("Waiting for Start Time");
234 case JS_WaitPriority:
235 jobstat = _("Waiting on Priority");
237 case JS_DataCommitting:
238 jobstat = _("SD committing Data");
240 case JS_DataDespooling:
241 jobstat = _("SD despooling Data");
243 case JS_AttrDespooling:
244 jobstat = _("SD despooling Attributes");
246 case JS_AttrInserting:
247 jobstat = _("Dir inserting Attributes");
251 if (JobStatus == 0) {
254 bsnprintf(buf, sizeof(buf), _("Unknown Job termination status=%d"), JobStatus);
259 bstrncpy(msg, jobstat, maxlen);
263 * Convert a JobStatus code into a human readable form - gui version
265 void jobstatus_to_ascii_gui(int JobStatus, char *msg, int maxlen)
267 const char *cnv = NULL;
270 cnv = _("Completed successfully");
273 cnv = _("Completed with warnings");
275 case JS_ErrorTerminated:
276 cnv = _("Terminated with errors");
279 cnv = _("Fatal error");
282 cnv = _("Created, not yet running");
285 cnv = _("Canceled by user");
288 cnv = _("Verify found differences");
291 cnv = _("Waiting for File daemon");
294 cnv = _("Waiting for Storage daemon");
296 case JS_WaitPriority:
297 cnv = _("Waiting for higher priority jobs");
299 case JS_AttrInserting:
300 cnv = _("Batch inserting file records");
305 bstrncpy(msg, cnv, maxlen);
307 jobstatus_to_ascii(JobStatus, msg, maxlen);
313 * Convert Job Termination Status into a string
315 const char *job_status_to_str(int stat)
324 str = _("OK -- with warnings");
326 case JS_ErrorTerminated:
331 str = _("Fatal Error");
337 str = _("Differences");
340 str = _("Unknown term code");
348 * Convert Job Type into a string
350 const char *job_type_to_str(int type)
352 const char *str = NULL;
358 case JT_MIGRATED_JOB:
359 str = _("Migrated Job");
371 str = _("System or Console");
393 str = _("Unknown Type");
398 /* Convert ActionOnPurge to string (Truncate, Erase, Destroy)
400 char *action_on_purge_to_string(int aop, POOL_MEM &ret)
402 if (aop & ON_PURGE_TRUNCATE) {
403 pm_strcpy(ret, _("Truncate"));
406 pm_strcpy(ret, _("None"));
412 * Convert Job Level into a string
414 const char *job_level_to_str(int level)
425 str = _("Incremental");
428 str = _("Differential");
433 case L_VERIFY_CATALOG:
434 str = _("Verify Catalog");
437 str = _("Verify Init Catalog");
439 case L_VERIFY_VOLUME_TO_CATALOG:
440 str = _("Verify Volume to Catalog");
442 case L_VERIFY_DISK_TO_CATALOG:
443 str = _("Verify Disk to Catalog");
446 str = _("Verify Data");
449 str = _("Virtual Full");
455 str = _("Unknown Job Level");
461 const char *volume_status_to_str(const char *status)
465 NT_("Append"), _("Append"),
466 NT_("Archive"), _("Archive"),
467 NT_("Disabled"), _("Disabled"),
468 NT_("Full"), _("Full"),
469 NT_("Used"), _("Used"),
470 NT_("Cleaning"), _("Cleaning"),
471 NT_("Purged"), _("Purged"),
472 NT_("Recycle"), _("Recycle"),
473 NT_("Read-Only"), _("Read-Only"),
474 NT_("Error"), _("Error"),
478 for (pos = 0 ; vs[pos] ; pos += 2) {
479 if ( !strcmp(vs[pos],status) ) {
485 return _("Invalid volume status");
489 /***********************************************************************
490 * Encode the mode bits into a 10 character string like LS does
491 ***********************************************************************/
493 char *encode_mode(mode_t mode, char *buf)
497 *cp++ = S_ISDIR(mode) ? 'd' : S_ISBLK(mode) ? 'b' : S_ISCHR(mode) ? 'c' :
498 S_ISLNK(mode) ? 'l' : S_ISFIFO(mode) ? 'f' : S_ISSOCK(mode) ? 's' : '-';
499 *cp++ = mode & S_IRUSR ? 'r' : '-';
500 *cp++ = mode & S_IWUSR ? 'w' : '-';
501 *cp++ = (mode & S_ISUID
502 ? (mode & S_IXUSR ? 's' : 'S')
503 : (mode & S_IXUSR ? 'x' : '-'));
504 *cp++ = mode & S_IRGRP ? 'r' : '-';
505 *cp++ = mode & S_IWGRP ? 'w' : '-';
506 *cp++ = (mode & S_ISGID
507 ? (mode & S_IXGRP ? 's' : 'S')
508 : (mode & S_IXGRP ? 'x' : '-'));
509 *cp++ = mode & S_IROTH ? 'r' : '-';
510 *cp++ = mode & S_IWOTH ? 'w' : '-';
511 *cp++ = (mode & S_ISVTX
512 ? (mode & S_IXOTH ? 't' : 'T')
513 : (mode & S_IXOTH ? 'x' : '-'));
518 #if defined(HAVE_WIN32)
519 int do_shell_expansion(char *name, int name_len)
521 char *src = bstrdup(name);
523 ExpandEnvironmentStrings(src, name, name_len);
530 int do_shell_expansion(char *name, int name_len)
532 static char meta[] = "~\\$[]*?`'<>\"";
537 char line[MAXSTRING];
538 const char *shellcmd;
540 /* Check if any meta characters are present */
542 for (i = 0; i < len; i++) {
543 if (strchr(name, meta[i])) {
549 cmd = get_pool_memory(PM_FNAME);
551 if ((shellcmd = getenv("SHELL")) == NULL) {
552 shellcmd = "/bin/sh";
554 pm_strcpy(&cmd, shellcmd);
555 pm_strcat(&cmd, " -c \"echo ");
556 pm_strcat(&cmd, name);
557 pm_strcat(&cmd, "\"");
558 Dmsg1(400, "Send: %s\n", cmd);
559 if ((bpipe = open_bpipe(cmd, 0, "r"))) {
561 fgets(line, sizeof(line), bpipe->rfd);
562 strip_trailing_junk(line);
563 stat = close_bpipe(bpipe);
564 Dmsg2(400, "stat=%d got: %s\n", stat, line);
566 stat = 1; /* error */
568 free_pool_memory(cmd);
570 bstrncpy(name, line, name_len);
578 /* MAKESESSIONKEY -- Generate session key with optional start
579 key. If mode is TRUE, the key will be
580 translated to a string, otherwise it is
581 returned as 16 binary bytes.
583 from SpeakFreely by John Walker */
585 void make_session_key(char *key, char *seed, int mode)
588 struct MD5Context md5c;
589 unsigned char md5key[16], md5key1[16];
596 bstrncat(s, seed, sizeof(s));
599 /* The following creates a seed for the session key generator
600 based on a collection of volatile and environment-specific
601 information unlikely to be vulnerable (as a whole) to an
602 exhaustive search attack. If one of these items isn't
603 available on your machine, replace it with something
604 equivalent or, if you like, just delete it. */
606 #if defined(HAVE_WIN32)
614 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)GetCurrentProcessId());
615 (void)getcwd(s + strlen(s), 256);
616 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)GetTickCount());
617 QueryPerformanceCounter(&li);
618 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)li.LowPart);
619 GetSystemTimeAsFileTime(&ft);
620 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)ft.dwLowDateTime);
621 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)ft.dwHighDateTime);
623 GetComputerName(s + strlen(s), &length);
625 GetUserName(s + strlen(s), &length);
628 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getpid());
629 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getppid());
630 (void)getcwd(s + strlen(s), 256);
631 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)clock());
632 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)time(NULL));
634 sysinfo(SI_HW_SERIAL,s + strlen(s), 12);
636 #if defined(HAVE_GETHOSTID)
637 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t) gethostid());
639 gethostname(s + strlen(s), 256);
640 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getuid());
641 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getgid());
644 MD5Update(&md5c, (uint8_t *)s, strlen(s));
645 MD5Final(md5key, &md5c);
646 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)((time(NULL) + 65121) ^ 0x375F));
648 MD5Update(&md5c, (uint8_t *)s, strlen(s));
649 MD5Final(md5key1, &md5c);
650 #define nextrand (md5key[j] ^ md5key1[j])
652 for (j = k = 0; j < 16; j++) {
653 unsigned char rb = nextrand;
655 #define Rad16(x) ((x) + 'A')
656 key[k++] = Rad16((rb >> 4) & 0xF);
657 key[k++] = Rad16(rb & 0xF);
665 for (j = 0; j < 16; j++) {
672 void encode_session_key(char *encode, char *session, char *key, int maxlen)
675 for (i=0; (i < maxlen-1) && session[i]; i++) {
676 if (session[i] == '-') {
679 encode[i] = ((session[i] - 'A' + key[i]) & 0xF) + 'A';
683 Dmsg3(000, "Session=%s key=%s encode=%s\n", session, key, encode);
686 void decode_session_key(char *decode, char *session, char *key, int maxlen)
690 for (i=0; (i < maxlen-1) && session[i]; i++) {
691 if (session[i] == '-') {
694 x = (session[i] - 'A' - key[i]) & 0xF;
702 Dmsg3(000, "Session=%s key=%s decode=%s\n", session, key, decode);
708 * Edit job codes into main command line
711 * %d = Director's name
716 * %n = Unadorned Job name
718 * %t = Job type (Backup, ...)
724 * omsg = edited output message
725 * imsg = input string containing edit codes (%x)
726 * to = recepients list
729 POOLMEM *edit_job_codes(JCR *jcr, char *omsg, char *imsg, const char *to, job_code_callback_t callback)
734 char name[MAX_NAME_LENGTH];
738 Dmsg1(200, "edit_job_codes: %s\n", imsg);
739 for (p=imsg; *p; p++) {
747 str = jcr->client_name;
753 str = my_name; /* Director's name */
757 str = job_status_to_str(jcr->JobStatus);
764 bsnprintf(add, sizeof(add), "%d", jcr->JobId);
770 case 'j': /* Job name */
779 str = job_level_to_str(jcr->getJobLevel());
786 bstrncpy(name, jcr->Job, sizeof(name));
787 /* There are three periods after the Job name */
788 for (i=0; i<3; i++) {
789 if ((q=strrchr(name, '.')) != NULL) {
801 case 's': /* since time */
802 if (jcr && jcr->stime) {
808 case 'f': /* Job Files */
809 str = edit_uint64(jcr->JobFiles, add);
811 case 'b': /* Job Bytes */
812 str = edit_uint64(jcr->JobBytes, add);
816 str = job_type_to_str(jcr->getJobType());
823 if (jcr->VolumeName && jcr->VolumeName[0]) {
824 str = jcr->VolumeName;
834 if (callback != NULL) {
835 str = callback(jcr, p);
851 Dmsg1(1200, "add_str %s\n", str);
852 pm_strcat(&omsg, str);
853 Dmsg1(1200, "omsg=%s\n", omsg);
858 void set_working_directory(char *wd)
860 struct stat stat_buf;
863 Emsg0(M_ERROR_TERM, 0, _("Working directory not defined. Cannot continue.\n"));
865 if (stat(wd, &stat_buf) != 0) {
866 Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" not found. Cannot continue.\n"),
869 if (!S_ISDIR(stat_buf.st_mode)) {
870 Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" is not a directory. Cannot continue.\n"),
873 working_directory = wd; /* set global */
876 const char *last_path_separator(const char *str)
879 for (const char *p = &str[strlen(str) - 1]; p >= str; p--) {
880 if (IsPathSeparator(*p)) {