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 /* kludge below disabled for MinGW, due to link problems ... */
124 #if HAVE_WIN32 && !HAVE_CONSOLE && !HAVE_WXCONSOLE && !HAVE_MINGW
125 extern long _timezone;
126 extern int _daylight;
127 extern long _dstbias;
128 extern "C" void __tzset(void);
129 extern "C" int _isindst(struct tm *);
132 char *encode_time(time_t time, char *buf)
137 #if HAVE_WIN32 && !HAVE_CONSOLE && !HAVE_WXCONSOLE && !HAVE_MINGW
139 * Gross kludge to avoid a seg fault in Microsoft's CRT localtime_r(),
140 * which incorrectly references a NULL returned from gmtime() if
141 * the time (adjusted for the current timezone) is invalid.
142 * This could happen if you have a bad date/time, or perhaps if you
143 * moved a file from one timezone to another?
148 gtime = time - _timezone;
149 if (!(gtm = gmtime(>ime))) {
152 if (_daylight && _isindst(gtm)) {
154 if (!gmtime(>ime)) {
159 if (localtime_r(&time, &tm)) {
160 n = sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d",
161 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
162 tm.tm_hour, tm.tm_min, tm.tm_sec);
170 * Convert a JobStatus code into a human readable form
172 void jobstatus_to_ascii(int JobStatus, char *msg, int maxlen)
179 jobstat = _("Created");
182 jobstat = _("Running");
185 jobstat = _("Blocked");
191 case JS_ErrorTerminated:
192 jobstat = _("Error");
195 jobstat = _("Non-fatal error");
198 jobstat = _("Canceled");
201 jobstat = _("Verify differences");
204 jobstat = _("Waiting on FD");
207 jobstat = _("Wait on SD");
210 jobstat = _("Wait for new Volume");
213 jobstat = _("Waiting for mount");
215 case JS_WaitStoreRes:
216 jobstat = _("Waiting for Storage resource");
219 jobstat = _("Waiting for Job resource");
221 case JS_WaitClientRes:
222 jobstat = _("Waiting for Client resource");
225 jobstat = _("Waiting on Max Jobs");
227 case JS_WaitStartTime:
228 jobstat = _("Waiting for Start Time");
230 case JS_WaitPriority:
231 jobstat = _("Waiting on Priority");
235 if (JobStatus == 0) {
238 bsnprintf(buf, sizeof(buf), _("Unknown Job termination status=%d"), JobStatus);
243 bstrncpy(msg, jobstat, maxlen);
247 * Convert Job Termination Status into a string
249 const char *job_status_to_str(int stat)
257 case JS_ErrorTerminated:
262 str = _("Fatal Error");
268 str = _("Differences");
271 str = _("Unknown term code");
279 * Convert Job Type into a string
281 const char *job_type_to_str(int type)
305 str = _("Unknown Type");
312 * Convert Job Level into a string
314 const char *job_level_to_str(int level)
325 str = _("Incremental");
328 str = _("Differential");
333 case L_VERIFY_CATALOG:
334 str = _("Verify Catalog");
337 str = _("Verify Init Catalog");
339 case L_VERIFY_VOLUME_TO_CATALOG:
340 str = _("Verify Volume to Catalog");
342 case L_VERIFY_DISK_TO_CATALOG:
343 str = _("Verify Disk to Catalog");
346 str = _("Verify Data");
352 str = _("Unknown Job Level");
359 /***********************************************************************
360 * Encode the mode bits into a 10 character string like LS does
361 ***********************************************************************/
363 char *encode_mode(mode_t mode, char *buf)
367 *cp++ = S_ISDIR(mode) ? 'd' : S_ISBLK(mode) ? 'b' : S_ISCHR(mode) ? 'c' :
368 S_ISLNK(mode) ? 'l' : S_ISFIFO(mode) ? 'f' : S_ISSOCK(mode) ? 's' : '-';
369 *cp++ = mode & S_IRUSR ? 'r' : '-';
370 *cp++ = mode & S_IWUSR ? 'w' : '-';
371 *cp++ = (mode & S_ISUID
372 ? (mode & S_IXUSR ? 's' : 'S')
373 : (mode & S_IXUSR ? 'x' : '-'));
374 *cp++ = mode & S_IRGRP ? 'r' : '-';
375 *cp++ = mode & S_IWGRP ? 'w' : '-';
376 *cp++ = (mode & S_ISGID
377 ? (mode & S_IXGRP ? 's' : 'S')
378 : (mode & S_IXGRP ? 'x' : '-'));
379 *cp++ = mode & S_IROTH ? 'r' : '-';
380 *cp++ = mode & S_IWOTH ? 'w' : '-';
381 *cp++ = (mode & S_ISVTX
382 ? (mode & S_IXOTH ? 't' : 'T')
383 : (mode & S_IXOTH ? 'x' : '-'));
389 int do_shell_expansion(char *name, int name_len)
391 static char meta[] = "~\\$[]*?`'<>\"";
396 char line[MAXSTRING];
397 const char *shellcmd;
399 /* Check if any meta characters are present */
401 for (i = 0; i < len; i++) {
402 if (strchr(name, meta[i])) {
408 cmd = get_pool_memory(PM_FNAME);
410 if ((shellcmd = getenv("SHELL")) == NULL) {
411 shellcmd = "/bin/sh";
413 pm_strcpy(&cmd, shellcmd);
414 pm_strcat(&cmd, " -c \"echo ");
415 pm_strcat(&cmd, name);
416 pm_strcat(&cmd, "\"");
417 Dmsg1(400, "Send: %s\n", cmd);
418 if ((bpipe = open_bpipe(cmd, 0, "r"))) {
420 fgets(line, sizeof(line), bpipe->rfd);
421 strip_trailing_junk(line);
422 stat = close_bpipe(bpipe);
423 Dmsg2(400, "stat=%d got: %s\n", stat, line);
425 stat = 1; /* error */
427 free_pool_memory(cmd);
429 bstrncpy(name, line, name_len);
436 /* MAKESESSIONKEY -- Generate session key with optional start
437 key. If mode is TRUE, the key will be
438 translated to a string, otherwise it is
439 returned as 16 binary bytes.
441 from SpeakFreely by John Walker */
443 void make_session_key(char *key, char *seed, int mode)
446 struct MD5Context md5c;
447 unsigned char md5key[16], md5key1[16];
452 bstrncat(s, seed, sizeof(s));
455 /* The following creates a seed for the session key generator
456 based on a collection of volatile and environment-specific
457 information unlikely to be vulnerable (as a whole) to an
458 exhaustive search attack. If one of these items isn't
459 available on your machine, replace it with something
460 equivalent or, if you like, just delete it. */
462 sprintf(s + strlen(s), "%lu", (unsigned long)getpid());
463 sprintf(s + strlen(s), "%lu", (unsigned long)getppid());
464 (void)getcwd(s + strlen(s), 256);
465 sprintf(s + strlen(s), "%lu", (unsigned long)clock());
466 sprintf(s + strlen(s), "%lu", (unsigned long)time(NULL));
468 sysinfo(SI_HW_SERIAL,s + strlen(s), 12);
470 #ifdef HAVE_GETHOSTID
471 sprintf(s + strlen(s), "%lu", (unsigned long) gethostid());
473 gethostname(s + strlen(s), 256);
474 sprintf(s + strlen(s), "%u", (unsigned)getuid());
475 sprintf(s + strlen(s), "%u", (unsigned)getgid());
477 MD5Update(&md5c, (unsigned char *)s, strlen(s));
478 MD5Final(md5key, &md5c);
479 sprintf(s + strlen(s), "%lu", (unsigned long)((time(NULL) + 65121) ^ 0x375F));
481 MD5Update(&md5c, (unsigned char *)s, strlen(s));
482 MD5Final(md5key1, &md5c);
483 #define nextrand (md5key[j] ^ md5key1[j])
485 for (j = k = 0; j < 16; j++) {
486 unsigned char rb = nextrand;
488 #define Rad16(x) ((x) + 'A')
489 key[k++] = Rad16((rb >> 4) & 0xF);
490 key[k++] = Rad16(rb & 0xF);
498 for (j = 0; j < 16; j++) {
508 * Edit job codes into main command line
511 * %d = Director's name
516 * %n = Unadorned Job name
518 * %t = Job type (Backup, ...)
522 * omsg = edited output message
523 * imsg = input string containing edit codes (%x)
524 * to = recepients list
527 POOLMEM *edit_job_codes(JCR *jcr, char *omsg, char *imsg, const char *to)
532 char name[MAX_NAME_LENGTH];
536 Dmsg1(200, "edit_job_codes: %s\n", imsg);
537 for (p=imsg; *p; p++) {
545 str = jcr->client_name;
551 str = my_name; /* Director's name */
555 str = job_status_to_str(jcr->JobStatus);
562 bsnprintf(add, sizeof(add), "%d", jcr->JobId);
568 case 'j': /* Job name */
577 str = job_level_to_str(jcr->JobLevel);
584 bstrncpy(name, jcr->Job, sizeof(name));
585 /* There are three periods after the Job name */
586 for (i=0; i<3; i++) {
587 if ((q=strrchr(name, '.')) != NULL) {
599 case 's': /* since time */
600 if (jcr && jcr->stime) {
608 str = job_type_to_str(jcr->JobType);
615 if (jcr->VolumeName && jcr->VolumeName[0]) {
616 str = jcr->VolumeName;
636 Dmsg1(1200, "add_str %s\n", str);
637 pm_strcat(&omsg, str);
638 Dmsg1(1200, "omsg=%s\n", omsg);
643 void set_working_directory(char *wd)
645 struct stat stat_buf;
648 Emsg0(M_ERROR_TERM, 0, _("Working directory not defined. Cannot continue.\n"));
650 if (stat(wd, &stat_buf) != 0) {
651 Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" not found. Cannot continue.\n"),
654 if (!S_ISDIR(stat_buf.st_mode)) {
655 Emsg1(M_ERROR_TERM, 0, _("Working Directory: \"%s\" is not a directory. Cannot continue.\n"),
658 working_directory = wd; /* set global */