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)
368 str = _("System or Console");
374 str = _("Unknown Type");
381 * Convert Job Level into a string
383 const char *job_level_to_str(int level)
394 str = _("Incremental");
397 str = _("Differential");
402 case L_VERIFY_CATALOG:
403 str = _("Verify Catalog");
406 str = _("Verify Init Catalog");
408 case L_VERIFY_VOLUME_TO_CATALOG:
409 str = _("Verify Volume to Catalog");
411 case L_VERIFY_DISK_TO_CATALOG:
412 str = _("Verify Disk to Catalog");
415 str = _("Verify Data");
418 str = _("Virtual Full");
424 str = _("Unknown Job Level");
430 const char *volume_status_to_str(const char *status)
434 NT_("Append"), _("Append"),
435 NT_("Archive"), _("Archive"),
436 NT_("Disabled"), _("Disabled"),
437 NT_("Full"), _("Full"),
438 NT_("Used"), _("Used"),
439 NT_("Cleaning"), _("Cleaning"),
440 NT_("Purged"), _("Purged"),
441 NT_("Recycle"), _("Recycle"),
442 NT_("Read-Only"), _("Read-Only"),
443 NT_("Error"), _("Error"),
447 for (pos = 0 ; vs[pos] ; pos += 2) {
448 if ( !strcmp(vs[pos],status) ) {
454 return _("Invalid volume status");
458 /***********************************************************************
459 * Encode the mode bits into a 10 character string like LS does
460 ***********************************************************************/
462 char *encode_mode(mode_t mode, char *buf)
466 *cp++ = S_ISDIR(mode) ? 'd' : S_ISBLK(mode) ? 'b' : S_ISCHR(mode) ? 'c' :
467 S_ISLNK(mode) ? 'l' : S_ISFIFO(mode) ? 'f' : S_ISSOCK(mode) ? 's' : '-';
468 *cp++ = mode & S_IRUSR ? 'r' : '-';
469 *cp++ = mode & S_IWUSR ? 'w' : '-';
470 *cp++ = (mode & S_ISUID
471 ? (mode & S_IXUSR ? 's' : 'S')
472 : (mode & S_IXUSR ? 'x' : '-'));
473 *cp++ = mode & S_IRGRP ? 'r' : '-';
474 *cp++ = mode & S_IWGRP ? 'w' : '-';
475 *cp++ = (mode & S_ISGID
476 ? (mode & S_IXGRP ? 's' : 'S')
477 : (mode & S_IXGRP ? 'x' : '-'));
478 *cp++ = mode & S_IROTH ? 'r' : '-';
479 *cp++ = mode & S_IWOTH ? 'w' : '-';
480 *cp++ = (mode & S_ISVTX
481 ? (mode & S_IXOTH ? 't' : 'T')
482 : (mode & S_IXOTH ? 'x' : '-'));
487 #if defined(HAVE_WIN32)
488 int do_shell_expansion(char *name, int name_len)
490 char *src = bstrdup(name);
492 ExpandEnvironmentStrings(src, name, name_len);
499 int do_shell_expansion(char *name, int name_len)
501 static char meta[] = "~\\$[]*?`'<>\"";
506 char line[MAXSTRING];
507 const char *shellcmd;
509 /* Check if any meta characters are present */
511 for (i = 0; i < len; i++) {
512 if (strchr(name, meta[i])) {
518 cmd = get_pool_memory(PM_FNAME);
520 if ((shellcmd = getenv("SHELL")) == NULL) {
521 shellcmd = "/bin/sh";
523 pm_strcpy(&cmd, shellcmd);
524 pm_strcat(&cmd, " -c \"echo ");
525 pm_strcat(&cmd, name);
526 pm_strcat(&cmd, "\"");
527 Dmsg1(400, "Send: %s\n", cmd);
528 if ((bpipe = open_bpipe(cmd, 0, "r"))) {
530 fgets(line, sizeof(line), bpipe->rfd);
531 strip_trailing_junk(line);
532 stat = close_bpipe(bpipe);
533 Dmsg2(400, "stat=%d got: %s\n", stat, line);
535 stat = 1; /* error */
537 free_pool_memory(cmd);
539 bstrncpy(name, line, name_len);
547 /* MAKESESSIONKEY -- Generate session key with optional start
548 key. If mode is TRUE, the key will be
549 translated to a string, otherwise it is
550 returned as 16 binary bytes.
552 from SpeakFreely by John Walker */
554 void make_session_key(char *key, char *seed, int mode)
557 struct MD5Context md5c;
558 unsigned char md5key[16], md5key1[16];
565 bstrncat(s, seed, sizeof(s));
568 /* The following creates a seed for the session key generator
569 based on a collection of volatile and environment-specific
570 information unlikely to be vulnerable (as a whole) to an
571 exhaustive search attack. If one of these items isn't
572 available on your machine, replace it with something
573 equivalent or, if you like, just delete it. */
575 #if defined(HAVE_WIN32)
583 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)GetCurrentProcessId());
584 (void)getcwd(s + strlen(s), 256);
585 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)GetTickCount());
586 QueryPerformanceCounter(&li);
587 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)li.LowPart);
588 GetSystemTimeAsFileTime(&ft);
589 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)ft.dwLowDateTime);
590 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)ft.dwHighDateTime);
592 GetComputerName(s + strlen(s), &length);
594 GetUserName(s + strlen(s), &length);
597 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getpid());
598 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getppid());
599 (void)getcwd(s + strlen(s), 256);
600 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)clock());
601 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)time(NULL));
603 sysinfo(SI_HW_SERIAL,s + strlen(s), 12);
605 #if defined(HAVE_GETHOSTID)
606 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t) gethostid());
608 gethostname(s + strlen(s), 256);
609 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getuid());
610 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getgid());
613 MD5Update(&md5c, (uint8_t *)s, strlen(s));
614 MD5Final(md5key, &md5c);
615 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)((time(NULL) + 65121) ^ 0x375F));
617 MD5Update(&md5c, (uint8_t *)s, strlen(s));
618 MD5Final(md5key1, &md5c);
619 #define nextrand (md5key[j] ^ md5key1[j])
621 for (j = k = 0; j < 16; j++) {
622 unsigned char rb = nextrand;
624 #define Rad16(x) ((x) + 'A')
625 key[k++] = Rad16((rb >> 4) & 0xF);
626 key[k++] = Rad16(rb & 0xF);
634 for (j = 0; j < 16; j++) {
641 void encode_session_key(char *encode, char *session, char *key, int maxlen)
644 for (i=0; (i < maxlen-1) && session[i]; i++) {
645 if (session[i] == '-') {
648 encode[i] = ((session[i] - 'A' + key[i]) & 0xF) + 'A';
652 Dmsg3(000, "Session=%s key=%s encode=%s\n", session, key, encode);
655 void decode_session_key(char *decode, char *session, char *key, int maxlen)
659 for (i=0; (i < maxlen-1) && session[i]; i++) {
660 if (session[i] == '-') {
663 x = (session[i] - 'A' - key[i]) & 0xF;
671 Dmsg3(000, "Session=%s key=%s decode=%s\n", session, key, decode);
677 * Edit job codes into main command line
680 * %d = Director's name
685 * %n = Unadorned Job name
687 * %t = Job type (Backup, ...)
691 * omsg = edited output message
692 * imsg = input string containing edit codes (%x)
693 * to = recepients list
696 POOLMEM *edit_job_codes(JCR *jcr, char *omsg, char *imsg, const char *to, job_code_callback_t callback)
701 char name[MAX_NAME_LENGTH];
705 Dmsg1(200, "edit_job_codes: %s\n", imsg);
706 for (p=imsg; *p; p++) {
714 str = jcr->client_name;
720 str = my_name; /* Director's name */
724 str = job_status_to_str(jcr->JobStatus);
731 bsnprintf(add, sizeof(add), "%d", jcr->JobId);
737 case 'j': /* Job name */
746 str = job_level_to_str(jcr->get_JobLevel());
753 bstrncpy(name, jcr->Job, sizeof(name));
754 /* There are three periods after the Job name */
755 for (i=0; i<3; i++) {
756 if ((q=strrchr(name, '.')) != NULL) {
768 case 's': /* since time */
769 if (jcr && jcr->stime) {
777 str = job_type_to_str(jcr->get_JobType());
784 if (jcr->VolumeName && jcr->VolumeName[0]) {
785 str = jcr->VolumeName;
795 if (callback != NULL) {
796 str = callback(jcr, p);
812 Dmsg1(1200, "add_str %s\n", str);
813 pm_strcat(&omsg, str);
814 Dmsg1(1200, "omsg=%s\n", omsg);
819 void set_working_directory(char *wd)
821 struct stat stat_buf;
824 Emsg0(M_ERROR_TERM, 0, _("Working directory not defined. Cannot continue.\n"));
826 if (stat(wd, &stat_buf) != 0) {
827 Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" not found. Cannot continue.\n"),
830 if (!S_ISDIR(stat_buf.st_mode)) {
831 Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" is not a directory. Cannot continue.\n"),
834 working_directory = wd; /* set global */
837 const char *last_path_separator(const char *str)
840 for (const char *p = &str[strlen(str) - 1]; p >= str; p--) {
841 if (IsPathSeparator(*p)) {