2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2008 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 two of the GNU 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 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 = _("Canceled");
202 jobstat = _("Verify differences");
205 jobstat = _("Waiting on FD");
208 jobstat = _("Wait on SD");
211 jobstat = _("Wait for new Volume");
214 jobstat = _("Waiting for mount");
216 case JS_WaitStoreRes:
217 jobstat = _("Waiting for Storage resource");
220 jobstat = _("Waiting for Job resource");
222 case JS_WaitClientRes:
223 jobstat = _("Waiting for Client resource");
226 jobstat = _("Waiting on Max Jobs");
228 case JS_WaitStartTime:
229 jobstat = _("Waiting for Start Time");
231 case JS_WaitPriority:
232 jobstat = _("Waiting on Priority");
234 case JS_DataCommitting:
235 jobstat = _("SD committing Data");
237 case JS_DataDespooling:
238 jobstat = _("SD despooling Data");
240 case JS_AttrDespooling:
241 jobstat = _("SD despooling Attributes");
243 case JS_AttrInserting:
244 jobstat = _("Dir inserting Attributes");
248 if (JobStatus == 0) {
251 bsnprintf(buf, sizeof(buf), _("Unknown Job termination status=%d"), JobStatus);
256 bstrncpy(msg, jobstat, maxlen);
260 * Convert a JobStatus code into a human readable form - gui version
262 void jobstatus_to_ascii_gui(int JobStatus, char *msg, int maxlen)
264 const char *cnv = NULL;
267 cnv = _("Completed successfully");
269 case JS_ErrorTerminated:
270 cnv = _("Terminated with errors");
273 cnv = _("Fatal error");
276 cnv = _("Created, not yet running");
279 cnv = _("Canceled by user");
282 cnv = _("Verify found differences");
285 cnv = _("Waiting for File daemon");
288 cnv = _("Waiting for Storage daemon");
290 case JS_WaitPriority:
291 cnv = _("Waiting for higher priority jobs");
293 case JS_AttrInserting:
294 cnv = _("Batch inserting file records");
299 bstrncpy(msg, cnv, maxlen);
301 jobstatus_to_ascii( JobStatus, msg, maxlen);
307 * Convert Job Termination Status into a string
309 const char *job_status_to_str(int stat)
317 case JS_ErrorTerminated:
322 str = _("Fatal Error");
328 str = _("Differences");
331 str = _("Unknown term code");
339 * Convert Job Type into a string
341 const char *job_type_to_str(int type)
371 str = _("System or Console");
377 str = _("Unknown Type");
384 * Convert Job Level into a string
386 const char *job_level_to_str(int level)
397 str = _("Incremental");
400 str = _("Differential");
405 case L_VERIFY_CATALOG:
406 str = _("Verify Catalog");
409 str = _("Verify Init Catalog");
411 case L_VERIFY_VOLUME_TO_CATALOG:
412 str = _("Verify Volume to Catalog");
414 case L_VERIFY_DISK_TO_CATALOG:
415 str = _("Verify Disk to Catalog");
418 str = _("Verify Data");
421 str = _("Virtual Full");
427 str = _("Unknown Job Level");
433 const char *volume_status_to_str(const char *status)
437 NT_("Append"), _("Append"),
438 NT_("Archive"), _("Archive"),
439 NT_("Disabled"), _("Disabled"),
440 NT_("Full"), _("Full"),
441 NT_("Used"), _("Used"),
442 NT_("Cleaning"), _("Cleaning"),
443 NT_("Purged"), _("Purged"),
444 NT_("Recycle"), _("Recycle"),
445 NT_("Read-Only"), _("Read-Only"),
446 NT_("Error"), _("Error"),
450 for (pos = 0 ; vs[pos] ; pos += 2) {
451 if ( !strcmp(vs[pos],status) ) {
457 return _("Invalid volume status");
461 /***********************************************************************
462 * Encode the mode bits into a 10 character string like LS does
463 ***********************************************************************/
465 char *encode_mode(mode_t mode, char *buf)
469 *cp++ = S_ISDIR(mode) ? 'd' : S_ISBLK(mode) ? 'b' : S_ISCHR(mode) ? 'c' :
470 S_ISLNK(mode) ? 'l' : S_ISFIFO(mode) ? 'f' : S_ISSOCK(mode) ? 's' : '-';
471 *cp++ = mode & S_IRUSR ? 'r' : '-';
472 *cp++ = mode & S_IWUSR ? 'w' : '-';
473 *cp++ = (mode & S_ISUID
474 ? (mode & S_IXUSR ? 's' : 'S')
475 : (mode & S_IXUSR ? 'x' : '-'));
476 *cp++ = mode & S_IRGRP ? 'r' : '-';
477 *cp++ = mode & S_IWGRP ? 'w' : '-';
478 *cp++ = (mode & S_ISGID
479 ? (mode & S_IXGRP ? 's' : 'S')
480 : (mode & S_IXGRP ? 'x' : '-'));
481 *cp++ = mode & S_IROTH ? 'r' : '-';
482 *cp++ = mode & S_IWOTH ? 'w' : '-';
483 *cp++ = (mode & S_ISVTX
484 ? (mode & S_IXOTH ? 't' : 'T')
485 : (mode & S_IXOTH ? 'x' : '-'));
490 #if defined(HAVE_WIN32)
491 int do_shell_expansion(char *name, int name_len)
493 char *src = bstrdup(name);
495 ExpandEnvironmentStrings(src, name, name_len);
502 int do_shell_expansion(char *name, int name_len)
504 static char meta[] = "~\\$[]*?`'<>\"";
509 char line[MAXSTRING];
510 const char *shellcmd;
512 /* Check if any meta characters are present */
514 for (i = 0; i < len; i++) {
515 if (strchr(name, meta[i])) {
521 cmd = get_pool_memory(PM_FNAME);
523 if ((shellcmd = getenv("SHELL")) == NULL) {
524 shellcmd = "/bin/sh";
526 pm_strcpy(&cmd, shellcmd);
527 pm_strcat(&cmd, " -c \"echo ");
528 pm_strcat(&cmd, name);
529 pm_strcat(&cmd, "\"");
530 Dmsg1(400, "Send: %s\n", cmd);
531 if ((bpipe = open_bpipe(cmd, 0, "r"))) {
533 fgets(line, sizeof(line), bpipe->rfd);
534 strip_trailing_junk(line);
535 stat = close_bpipe(bpipe);
536 Dmsg2(400, "stat=%d got: %s\n", stat, line);
538 stat = 1; /* error */
540 free_pool_memory(cmd);
542 bstrncpy(name, line, name_len);
550 /* MAKESESSIONKEY -- Generate session key with optional start
551 key. If mode is TRUE, the key will be
552 translated to a string, otherwise it is
553 returned as 16 binary bytes.
555 from SpeakFreely by John Walker */
557 void make_session_key(char *key, char *seed, int mode)
560 struct MD5Context md5c;
561 unsigned char md5key[16], md5key1[16];
568 bstrncat(s, seed, sizeof(s));
571 /* The following creates a seed for the session key generator
572 based on a collection of volatile and environment-specific
573 information unlikely to be vulnerable (as a whole) to an
574 exhaustive search attack. If one of these items isn't
575 available on your machine, replace it with something
576 equivalent or, if you like, just delete it. */
578 #if defined(HAVE_WIN32)
586 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)GetCurrentProcessId());
587 (void)getcwd(s + strlen(s), 256);
588 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)GetTickCount());
589 QueryPerformanceCounter(&li);
590 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)li.LowPart);
591 GetSystemTimeAsFileTime(&ft);
592 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)ft.dwLowDateTime);
593 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)ft.dwHighDateTime);
595 GetComputerName(s + strlen(s), &length);
597 GetUserName(s + strlen(s), &length);
600 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getpid());
601 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getppid());
602 (void)getcwd(s + strlen(s), 256);
603 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)clock());
604 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)time(NULL));
606 sysinfo(SI_HW_SERIAL,s + strlen(s), 12);
608 #if defined(HAVE_GETHOSTID)
609 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t) gethostid());
611 gethostname(s + strlen(s), 256);
612 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getuid());
613 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getgid());
616 MD5Update(&md5c, (uint8_t *)s, strlen(s));
617 MD5Final(md5key, &md5c);
618 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)((time(NULL) + 65121) ^ 0x375F));
620 MD5Update(&md5c, (uint8_t *)s, strlen(s));
621 MD5Final(md5key1, &md5c);
622 #define nextrand (md5key[j] ^ md5key1[j])
624 for (j = k = 0; j < 16; j++) {
625 unsigned char rb = nextrand;
627 #define Rad16(x) ((x) + 'A')
628 key[k++] = Rad16((rb >> 4) & 0xF);
629 key[k++] = Rad16(rb & 0xF);
637 for (j = 0; j < 16; j++) {
644 void encode_session_key(char *encode, char *session, char *key, int maxlen)
647 for (i=0; (i < maxlen-1) && session[i]; i++) {
648 if (session[i] == '-') {
651 encode[i] = ((session[i] - 'A' + key[i]) & 0xF) + 'A';
655 Dmsg3(000, "Session=%s key=%s encode=%s\n", session, key, encode);
658 void decode_session_key(char *decode, char *session, char *key, int maxlen)
662 for (i=0; (i < maxlen-1) && session[i]; i++) {
663 if (session[i] == '-') {
666 x = (session[i] - 'A' - key[i]) & 0xF;
674 Dmsg3(000, "Session=%s key=%s decode=%s\n", session, key, decode);
680 * Edit job codes into main command line
683 * %d = Director's name
688 * %n = Unadorned Job name
690 * %t = Job type (Backup, ...)
694 * omsg = edited output message
695 * imsg = input string containing edit codes (%x)
696 * to = recepients list
699 POOLMEM *edit_job_codes(JCR *jcr, char *omsg, char *imsg, const char *to, job_code_callback_t callback)
704 char name[MAX_NAME_LENGTH];
708 Dmsg1(200, "edit_job_codes: %s\n", imsg);
709 for (p=imsg; *p; p++) {
717 str = jcr->client_name;
723 str = my_name; /* Director's name */
727 str = job_status_to_str(jcr->JobStatus);
734 bsnprintf(add, sizeof(add), "%d", jcr->JobId);
740 case 'j': /* Job name */
749 str = job_level_to_str(jcr->get_JobLevel());
756 bstrncpy(name, jcr->Job, sizeof(name));
757 /* There are three periods after the Job name */
758 for (i=0; i<3; i++) {
759 if ((q=strrchr(name, '.')) != NULL) {
771 case 's': /* since time */
772 if (jcr && jcr->stime) {
780 str = job_type_to_str(jcr->get_JobType());
787 if (jcr->VolumeName && jcr->VolumeName[0]) {
788 str = jcr->VolumeName;
798 if (callback != NULL) {
799 str = callback(jcr, p);
815 Dmsg1(1200, "add_str %s\n", str);
816 pm_strcat(&omsg, str);
817 Dmsg1(1200, "omsg=%s\n", omsg);
822 void set_working_directory(char *wd)
824 struct stat stat_buf;
827 Emsg0(M_ERROR_TERM, 0, _("Working directory not defined. Cannot continue.\n"));
829 if (stat(wd, &stat_buf) != 0) {
830 Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" not found. Cannot continue.\n"),
833 if (!S_ISDIR(stat_buf.st_mode)) {
834 Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" is not a directory. Cannot continue.\n"),
837 working_directory = wd; /* set global */
840 const char *last_path_separator(const char *str)
843 for (const char *p = &str[strlen(str) - 1]; p >= str; p--) {
844 if (IsPathSeparator(*p)) {