2 * util.c miscellaneous utility subroutines for Bacula
9 Copyright (C) 2000-2006 Kern Sibbald
11 This program is free software; you can redistribute it and/or
12 modify it under the terms of the GNU General Public License
13 version 2 as amended with additional clauses defined in the
14 file LICENSE in the main source directory.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 the file LICENSE for additional details.
25 #include "findlib/find.h"
28 * Various Bacula Utility subroutines
32 /* Return true of buffer has all zero bytes */
33 int is_buf_zero(char *buf, int len)
37 int i, len64, done, rem;
43 /* Optimize by checking uint64_t for zero */
44 len64 = len / sizeof(uint64_t);
45 for (i=0; i < len64; i++) {
50 done = len64 * sizeof(uint64_t); /* bytes already checked */
53 for (i = 0; i < rem; i++) {
62 /* Convert a string in place to lower case */
67 *str = tolower((int)(*str));
72 /* Convert spaces to non-space character.
73 * This makes scanf of fields containing spaces easier.
76 bash_spaces(char *str)
85 /* Convert spaces to non-space character.
86 * This makes scanf of fields containing spaces easier.
89 bash_spaces(POOL_MEM &pm)
91 char *str = pm.c_str();
100 /* Convert non-space characters (0x1) back into spaces */
102 unbash_spaces(char *str)
111 /* Convert non-space characters (0x1) back into spaces */
113 unbash_spaces(POOL_MEM &pm)
115 char *str = pm.c_str();
123 char *encode_time(time_t time, char *buf)
128 #if defined(HAVE_WIN32)
130 * Avoid a seg fault in Microsoft's CRT localtime_r(),
131 * which incorrectly references a NULL returned from gmtime() if
132 * time is negative before or after the timezone adjustment.
136 if ((gtm = gmtime(&time)) == NULL) {
140 if (gtm->tm_year == 1970 && gtm->tm_mon == 1 && gtm->tm_mday < 3) {
145 if (localtime_r(&time, &tm)) {
146 n = sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d",
147 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
148 tm.tm_hour, tm.tm_min, tm.tm_sec);
156 * Convert a JobStatus code into a human readable form
158 void jobstatus_to_ascii(int JobStatus, char *msg, int maxlen)
165 jobstat = _("Created");
168 jobstat = _("Running");
171 jobstat = _("Blocked");
177 case JS_ErrorTerminated:
178 jobstat = _("Error");
181 jobstat = _("Non-fatal error");
184 jobstat = _("Canceled");
187 jobstat = _("Verify differences");
190 jobstat = _("Waiting on FD");
193 jobstat = _("Wait on SD");
196 jobstat = _("Wait for new Volume");
199 jobstat = _("Waiting for mount");
201 case JS_WaitStoreRes:
202 jobstat = _("Waiting for Storage resource");
205 jobstat = _("Waiting for Job resource");
207 case JS_WaitClientRes:
208 jobstat = _("Waiting for Client resource");
211 jobstat = _("Waiting on Max Jobs");
213 case JS_WaitStartTime:
214 jobstat = _("Waiting for Start Time");
216 case JS_WaitPriority:
217 jobstat = _("Waiting on Priority");
221 if (JobStatus == 0) {
224 bsnprintf(buf, sizeof(buf), _("Unknown Job termination status=%d"), JobStatus);
229 bstrncpy(msg, jobstat, maxlen);
233 * Convert Job Termination Status into a string
235 const char *job_status_to_str(int stat)
243 case JS_ErrorTerminated:
248 str = _("Fatal Error");
254 str = _("Differences");
257 str = _("Unknown term code");
265 * Convert Job Type into a string
267 const char *job_type_to_str(int type)
294 str = _("System or Console");
300 str = _("Unknown Type");
307 * Convert Job Level into a string
309 const char *job_level_to_str(int level)
320 str = _("Incremental");
323 str = _("Differential");
328 case L_VERIFY_CATALOG:
329 str = _("Verify Catalog");
332 str = _("Verify Init Catalog");
334 case L_VERIFY_VOLUME_TO_CATALOG:
335 str = _("Verify Volume to Catalog");
337 case L_VERIFY_DISK_TO_CATALOG:
338 str = _("Verify Disk to Catalog");
341 str = _("Verify Data");
347 str = _("Unknown Job Level");
354 /***********************************************************************
355 * Encode the mode bits into a 10 character string like LS does
356 ***********************************************************************/
358 char *encode_mode(mode_t mode, char *buf)
362 *cp++ = S_ISDIR(mode) ? 'd' : S_ISBLK(mode) ? 'b' : S_ISCHR(mode) ? 'c' :
363 S_ISLNK(mode) ? 'l' : S_ISFIFO(mode) ? 'f' : S_ISSOCK(mode) ? 's' : '-';
364 *cp++ = mode & S_IRUSR ? 'r' : '-';
365 *cp++ = mode & S_IWUSR ? 'w' : '-';
366 *cp++ = (mode & S_ISUID
367 ? (mode & S_IXUSR ? 's' : 'S')
368 : (mode & S_IXUSR ? 'x' : '-'));
369 *cp++ = mode & S_IRGRP ? 'r' : '-';
370 *cp++ = mode & S_IWGRP ? 'w' : '-';
371 *cp++ = (mode & S_ISGID
372 ? (mode & S_IXGRP ? 's' : 'S')
373 : (mode & S_IXGRP ? 'x' : '-'));
374 *cp++ = mode & S_IROTH ? 'r' : '-';
375 *cp++ = mode & S_IWOTH ? 'w' : '-';
376 *cp++ = (mode & S_ISVTX
377 ? (mode & S_IXOTH ? 't' : 'T')
378 : (mode & S_IXOTH ? 'x' : '-'));
383 #if defined(HAVE_WIN32)
384 int do_shell_expansion(char *name, int name_len)
386 char *src = bstrdup(name);
388 ExpandEnvironmentStrings(src, name, name_len);
395 int do_shell_expansion(char *name, int name_len)
397 static char meta[] = "~\\$[]*?`'<>\"";
402 char line[MAXSTRING];
403 const char *shellcmd;
405 /* Check if any meta characters are present */
407 for (i = 0; i < len; i++) {
408 if (strchr(name, meta[i])) {
414 cmd = get_pool_memory(PM_FNAME);
416 if ((shellcmd = getenv("SHELL")) == NULL) {
417 shellcmd = "/bin/sh";
419 pm_strcpy(&cmd, shellcmd);
420 pm_strcat(&cmd, " -c \"echo ");
421 pm_strcat(&cmd, name);
422 pm_strcat(&cmd, "\"");
423 Dmsg1(400, "Send: %s\n", cmd);
424 if ((bpipe = open_bpipe(cmd, 0, "r"))) {
426 fgets(line, sizeof(line), bpipe->rfd);
427 strip_trailing_junk(line);
428 stat = close_bpipe(bpipe);
429 Dmsg2(400, "stat=%d got: %s\n", stat, line);
431 stat = 1; /* error */
433 free_pool_memory(cmd);
435 bstrncpy(name, line, name_len);
443 /* MAKESESSIONKEY -- Generate session key with optional start
444 key. If mode is TRUE, the key will be
445 translated to a string, otherwise it is
446 returned as 16 binary bytes.
448 from SpeakFreely by John Walker */
450 void make_session_key(char *key, char *seed, int mode)
453 struct MD5Context md5c;
454 unsigned char md5key[16], md5key1[16];
459 bstrncat(s, seed, sizeof(s));
462 /* The following creates a seed for the session key generator
463 based on a collection of volatile and environment-specific
464 information unlikely to be vulnerable (as a whole) to an
465 exhaustive search attack. If one of these items isn't
466 available on your machine, replace it with something
467 equivalent or, if you like, just delete it. */
469 #if defined(HAVE_WIN32)
477 sprintf(s + strlen(s), "%lu", (unsigned long)GetCurrentProcessId());
478 (void)getcwd(s + strlen(s), 256);
479 sprintf(s + strlen(s), "%lu", (unsigned long)GetTickCount());
480 QueryPerformanceCounter(&li);
481 sprintf(s + strlen(s), "%lu", (unsigned long)li.LowPart);
482 GetSystemTimeAsFileTime(&ft);
483 sprintf(s + strlen(s), "%lu", (unsigned long)ft.dwLowDateTime);
484 sprintf(s + strlen(s), "%lu", (unsigned long)ft.dwHighDateTime);
486 GetComputerName(s + strlen(s), &length);
488 GetUserName(s + strlen(s), &length);
491 sprintf(s + strlen(s), "%lu", (unsigned long)getpid());
492 sprintf(s + strlen(s), "%lu", (unsigned long)getppid());
493 (void)getcwd(s + strlen(s), 256);
494 sprintf(s + strlen(s), "%lu", (unsigned long)clock());
495 sprintf(s + strlen(s), "%lu", (unsigned long)time(NULL));
497 sysinfo(SI_HW_SERIAL,s + strlen(s), 12);
499 #if defined(HAVE_GETHOSTID)
500 sprintf(s + strlen(s), "%lu", (unsigned long) gethostid());
502 gethostname(s + strlen(s), 256);
503 sprintf(s + strlen(s), "%u", (unsigned)getuid());
504 sprintf(s + strlen(s), "%u", (unsigned)getgid());
507 MD5Update(&md5c, (unsigned char *)s, strlen(s));
508 MD5Final(md5key, &md5c);
509 sprintf(s + strlen(s), "%lu", (unsigned long)((time(NULL) + 65121) ^ 0x375F));
511 MD5Update(&md5c, (unsigned char *)s, strlen(s));
512 MD5Final(md5key1, &md5c);
513 #define nextrand (md5key[j] ^ md5key1[j])
515 for (j = k = 0; j < 16; j++) {
516 unsigned char rb = nextrand;
518 #define Rad16(x) ((x) + 'A')
519 key[k++] = Rad16((rb >> 4) & 0xF);
520 key[k++] = Rad16(rb & 0xF);
528 for (j = 0; j < 16; j++) {
538 * Edit job codes into main command line
541 * %d = Director's name
546 * %n = Unadorned Job name
548 * %t = Job type (Backup, ...)
552 * omsg = edited output message
553 * imsg = input string containing edit codes (%x)
554 * to = recepients list
557 POOLMEM *edit_job_codes(JCR *jcr, char *omsg, char *imsg, const char *to)
562 char name[MAX_NAME_LENGTH];
566 Dmsg1(200, "edit_job_codes: %s\n", imsg);
567 for (p=imsg; *p; p++) {
575 str = jcr->client_name;
581 str = my_name; /* Director's name */
585 str = job_status_to_str(jcr->JobStatus);
592 bsnprintf(add, sizeof(add), "%d", jcr->JobId);
598 case 'j': /* Job name */
607 str = job_level_to_str(jcr->JobLevel);
614 bstrncpy(name, jcr->Job, sizeof(name));
615 /* There are three periods after the Job name */
616 for (i=0; i<3; i++) {
617 if ((q=strrchr(name, '.')) != NULL) {
629 case 's': /* since time */
630 if (jcr && jcr->stime) {
638 str = job_type_to_str(jcr->JobType);
645 if (jcr->VolumeName && jcr->VolumeName[0]) {
646 str = jcr->VolumeName;
666 Dmsg1(1200, "add_str %s\n", str);
667 pm_strcat(&omsg, str);
668 Dmsg1(1200, "omsg=%s\n", omsg);
673 void set_working_directory(char *wd)
675 struct stat stat_buf;
678 Emsg0(M_ERROR_TERM, 0, _("Working directory not defined. Cannot continue.\n"));
680 if (stat(wd, &stat_buf) != 0) {
681 Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" not found. Cannot continue.\n"),
684 if (!S_ISDIR(stat_buf.st_mode)) {
685 Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" is not a directory. Cannot continue.\n"),
688 working_directory = wd; /* set global */