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 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 = _("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");
399 * Convert Job Level into a string
401 const char *job_level_to_str(int level)
412 str = _("Incremental");
415 str = _("Differential");
420 case L_VERIFY_CATALOG:
421 str = _("Verify Catalog");
424 str = _("Verify Init Catalog");
426 case L_VERIFY_VOLUME_TO_CATALOG:
427 str = _("Verify Volume to Catalog");
429 case L_VERIFY_DISK_TO_CATALOG:
430 str = _("Verify Disk to Catalog");
433 str = _("Verify Data");
436 str = _("Virtual Full");
442 str = _("Unknown Job Level");
448 const char *volume_status_to_str(const char *status)
452 NT_("Append"), _("Append"),
453 NT_("Archive"), _("Archive"),
454 NT_("Disabled"), _("Disabled"),
455 NT_("Full"), _("Full"),
456 NT_("Used"), _("Used"),
457 NT_("Cleaning"), _("Cleaning"),
458 NT_("Purged"), _("Purged"),
459 NT_("Recycle"), _("Recycle"),
460 NT_("Read-Only"), _("Read-Only"),
461 NT_("Error"), _("Error"),
465 for (pos = 0 ; vs[pos] ; pos += 2) {
466 if ( !strcmp(vs[pos],status) ) {
472 return _("Invalid volume status");
476 /***********************************************************************
477 * Encode the mode bits into a 10 character string like LS does
478 ***********************************************************************/
480 char *encode_mode(mode_t mode, char *buf)
484 *cp++ = S_ISDIR(mode) ? 'd' : S_ISBLK(mode) ? 'b' : S_ISCHR(mode) ? 'c' :
485 S_ISLNK(mode) ? 'l' : S_ISFIFO(mode) ? 'f' : S_ISSOCK(mode) ? 's' : '-';
486 *cp++ = mode & S_IRUSR ? 'r' : '-';
487 *cp++ = mode & S_IWUSR ? 'w' : '-';
488 *cp++ = (mode & S_ISUID
489 ? (mode & S_IXUSR ? 's' : 'S')
490 : (mode & S_IXUSR ? 'x' : '-'));
491 *cp++ = mode & S_IRGRP ? 'r' : '-';
492 *cp++ = mode & S_IWGRP ? 'w' : '-';
493 *cp++ = (mode & S_ISGID
494 ? (mode & S_IXGRP ? 's' : 'S')
495 : (mode & S_IXGRP ? 'x' : '-'));
496 *cp++ = mode & S_IROTH ? 'r' : '-';
497 *cp++ = mode & S_IWOTH ? 'w' : '-';
498 *cp++ = (mode & S_ISVTX
499 ? (mode & S_IXOTH ? 't' : 'T')
500 : (mode & S_IXOTH ? 'x' : '-'));
505 #if defined(HAVE_WIN32)
506 int do_shell_expansion(char *name, int name_len)
508 char *src = bstrdup(name);
510 ExpandEnvironmentStrings(src, name, name_len);
517 int do_shell_expansion(char *name, int name_len)
519 static char meta[] = "~\\$[]*?`'<>\"";
524 char line[MAXSTRING];
525 const char *shellcmd;
527 /* Check if any meta characters are present */
529 for (i = 0; i < len; i++) {
530 if (strchr(name, meta[i])) {
536 cmd = get_pool_memory(PM_FNAME);
538 if ((shellcmd = getenv("SHELL")) == NULL) {
539 shellcmd = "/bin/sh";
541 pm_strcpy(&cmd, shellcmd);
542 pm_strcat(&cmd, " -c \"echo ");
543 pm_strcat(&cmd, name);
544 pm_strcat(&cmd, "\"");
545 Dmsg1(400, "Send: %s\n", cmd);
546 if ((bpipe = open_bpipe(cmd, 0, "r"))) {
548 fgets(line, sizeof(line), bpipe->rfd);
549 strip_trailing_junk(line);
550 stat = close_bpipe(bpipe);
551 Dmsg2(400, "stat=%d got: %s\n", stat, line);
553 stat = 1; /* error */
555 free_pool_memory(cmd);
557 bstrncpy(name, line, name_len);
565 /* MAKESESSIONKEY -- Generate session key with optional start
566 key. If mode is TRUE, the key will be
567 translated to a string, otherwise it is
568 returned as 16 binary bytes.
570 from SpeakFreely by John Walker */
572 void make_session_key(char *key, char *seed, int mode)
575 struct MD5Context md5c;
576 unsigned char md5key[16], md5key1[16];
583 bstrncat(s, seed, sizeof(s));
586 /* The following creates a seed for the session key generator
587 based on a collection of volatile and environment-specific
588 information unlikely to be vulnerable (as a whole) to an
589 exhaustive search attack. If one of these items isn't
590 available on your machine, replace it with something
591 equivalent or, if you like, just delete it. */
593 #if defined(HAVE_WIN32)
601 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)GetCurrentProcessId());
602 (void)getcwd(s + strlen(s), 256);
603 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)GetTickCount());
604 QueryPerformanceCounter(&li);
605 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)li.LowPart);
606 GetSystemTimeAsFileTime(&ft);
607 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)ft.dwLowDateTime);
608 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)ft.dwHighDateTime);
610 GetComputerName(s + strlen(s), &length);
612 GetUserName(s + strlen(s), &length);
615 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getpid());
616 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getppid());
617 (void)getcwd(s + strlen(s), 256);
618 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)clock());
619 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)time(NULL));
621 sysinfo(SI_HW_SERIAL,s + strlen(s), 12);
623 #if defined(HAVE_GETHOSTID)
624 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t) gethostid());
626 gethostname(s + strlen(s), 256);
627 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getuid());
628 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getgid());
631 MD5Update(&md5c, (uint8_t *)s, strlen(s));
632 MD5Final(md5key, &md5c);
633 bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)((time(NULL) + 65121) ^ 0x375F));
635 MD5Update(&md5c, (uint8_t *)s, strlen(s));
636 MD5Final(md5key1, &md5c);
637 #define nextrand (md5key[j] ^ md5key1[j])
639 for (j = k = 0; j < 16; j++) {
640 unsigned char rb = nextrand;
642 #define Rad16(x) ((x) + 'A')
643 key[k++] = Rad16((rb >> 4) & 0xF);
644 key[k++] = Rad16(rb & 0xF);
652 for (j = 0; j < 16; j++) {
659 void encode_session_key(char *encode, char *session, char *key, int maxlen)
662 for (i=0; (i < maxlen-1) && session[i]; i++) {
663 if (session[i] == '-') {
666 encode[i] = ((session[i] - 'A' + key[i]) & 0xF) + 'A';
670 Dmsg3(000, "Session=%s key=%s encode=%s\n", session, key, encode);
673 void decode_session_key(char *decode, char *session, char *key, int maxlen)
677 for (i=0; (i < maxlen-1) && session[i]; i++) {
678 if (session[i] == '-') {
681 x = (session[i] - 'A' - key[i]) & 0xF;
689 Dmsg3(000, "Session=%s key=%s decode=%s\n", session, key, decode);
695 * Edit job codes into main command line
698 * %d = Director's name
703 * %n = Unadorned Job name
705 * %t = Job type (Backup, ...)
709 * omsg = edited output message
710 * imsg = input string containing edit codes (%x)
711 * to = recepients list
714 POOLMEM *edit_job_codes(JCR *jcr, char *omsg, char *imsg, const char *to, job_code_callback_t callback)
719 char name[MAX_NAME_LENGTH];
723 Dmsg1(200, "edit_job_codes: %s\n", imsg);
724 for (p=imsg; *p; p++) {
732 str = jcr->client_name;
738 str = my_name; /* Director's name */
742 str = job_status_to_str(jcr->JobStatus);
749 bsnprintf(add, sizeof(add), "%d", jcr->JobId);
755 case 'j': /* Job name */
764 str = job_level_to_str(jcr->get_JobLevel());
771 bstrncpy(name, jcr->Job, sizeof(name));
772 /* There are three periods after the Job name */
773 for (i=0; i<3; i++) {
774 if ((q=strrchr(name, '.')) != NULL) {
786 case 's': /* since time */
787 if (jcr && jcr->stime) {
795 str = job_type_to_str(jcr->get_JobType());
802 if (jcr->VolumeName && jcr->VolumeName[0]) {
803 str = jcr->VolumeName;
813 if (callback != NULL) {
814 str = callback(jcr, p);
830 Dmsg1(1200, "add_str %s\n", str);
831 pm_strcat(&omsg, str);
832 Dmsg1(1200, "omsg=%s\n", omsg);
837 void set_working_directory(char *wd)
839 struct stat stat_buf;
842 Emsg0(M_ERROR_TERM, 0, _("Working directory not defined. Cannot continue.\n"));
844 if (stat(wd, &stat_buf) != 0) {
845 Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" not found. Cannot continue.\n"),
848 if (!S_ISDIR(stat_buf.st_mode)) {
849 Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" is not a directory. Cannot continue.\n"),
852 working_directory = wd; /* set global */
855 const char *last_path_separator(const char *str)
858 for (const char *p = &str[strlen(str) - 1]; p >= str; p--) {
859 if (IsPathSeparator(*p)) {