/* * util.c - general utilities * * $Id: util.c 361 2006-04-19 19:32:22Z pw $ * * Copyright (C) 2000-6 Pete Wyckoff * * Distributed under the GNU Public License Version 2 or later (See LICENSE) */ #include #include #include #include #include #include #include #include #include #include /* pbse_to_txt found in pbs liblog.a */ #include "mpiexec.h" /* set to 1 to add a timestamp to each debug message */ #define DEBUG_TIMESTAMP 0 /* * Set the program name, first statement of code usually. */ void set_progname(int argc ATTR_UNUSED, const char *const argv[]) { const char *cp, *eptr; for (cp=progname=argv[0]; *cp; cp++) if (*cp == '/') progname = cp+1; /* figure out directory from whence mpiexec came, if invoked as such */ progname_dir = NULL; eptr = progname; if (eptr != argv[0]) { --eptr; if (*eptr == '/') { /* require and keep the directory slash */ int len = eptr - argv[0] + 1; progname_dir = Malloc(len + 1); strncpy(progname_dir, argv[0], len); progname_dir[len] = '\0'; } } } /* * Convert tm error numbers to strings. */ static const char * tm_errno_to_str(int err) { const struct { int err; const char *str; } tm_errs[] = { { TM_ESYSTEM, "system error" }, { TM_ENOEVENT, "no event" }, { TM_ENOTCONNECTED, "not connected" }, { TM_EUNKNOWNCMD, "unknown command" }, { TM_ENOTIMPLEMENTED, "not implemented" }, { TM_EBADENVIRONMENT, "bad environment" }, { TM_ENOTFOUND, "not found" }, { TM_BADINIT, "bad init" }, /* not my typo */ }; const int num_tm_errs = sizeof(tm_errs) / sizeof(tm_errs[0]); int i; for (i=0; iverbose >= level) { fprintf(stderr, "%s: ", progname); if (DEBUG_TIMESTAMP) { /* add a timestamp to debug messages */ struct timeval tv; char buf[20]; gettimeofday(&tv, NULL); strftime(buf, 10, "[%H:%M:%S", localtime(&tv.tv_sec)); sprintf(buf+9, ".%06ld] ", (long) tv.tv_usec); /* cast for mac */ fputs(buf, stderr); } va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fprintf(stderr, ".\n"); } } /* * Warning, non-fatal. */ void warning(const char *fmt, ...) { va_list ap; fprintf(stderr, "%s: Warning: ", progname); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fprintf(stderr, ".\n"); } /* * Warning, non-fatal, with the PBS tm errno message. */ void warning_tm(int err, const char *fmt, ...) { va_list ap; fprintf(stderr, "%s: Warning: ", progname); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); error_tm_print(err); } /* * Error, fatal. */ void error(const char *fmt, ...) { va_list ap; fprintf(stderr, "%s: Error: ", progname); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fprintf(stderr, ".\n"); try_kill_stdio(); /* never hurts */ exit(1); } /* * Error, fatal, with the errno message. */ void error_errno(const char *fmt, ...) { va_list ap; fprintf(stderr, "%s: Error: ", progname); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fprintf(stderr, ": %s.\n", strerror(errno)); try_kill_stdio(); exit(1); } static void error_pbs_print(int err) { const char *s; /* * Error message for PBSE_SYSTEM is weird: " Sytem error: ", so * replace it. */ if (err == PBSE_SYSTEM) fprintf(stderr, ": System error.\n"); else if ((s = pbse_to_txt(err))) fprintf(stderr, ": %s.\n", s); else if (pbs_errno < PBSE_) /* try standard error numbers, which it sometimes will return */ fprintf(stderr, ": %s.\n", strerror(pbs_errno)); else fprintf(stderr, ": Unknown PBS error %d.\n", err); } /* * Error, fatal, with the pbs_errno message. */ void error_pbs(const char *fmt, ...) { va_list ap; fprintf(stderr, "%s: Error: ", progname); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); error_pbs_print(pbs_errno); try_kill_stdio(); exit(1); } /* * Error, fatal, with the PBS tm errno message. */ void error_tm(int err, const char *fmt, ...) { va_list ap; fprintf(stderr, "%s: Error: ", progname); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); error_tm_print(err); try_kill_stdio(); exit(1); } /* * Check through both tm and pbs error numbers looking for a match. */ void error_tm_or_pbs(int err, const char *fmt, ...) { va_list ap; const char *s; fprintf(stderr, "%s: Error: ", progname); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); s = tm_errno_to_str(err); if (s) error_tm_print(err); else error_pbs_print(err); try_kill_stdio(); exit(1); } /* * Put a string in permanent memory. strdup with error-checking * malloc. */ char * strsave(const char *s) { char *t; t = Malloc((strlen(s)+1)*sizeof(char)); strcpy(t, s); return t; } /* * Error-checking malloc. */ void * Malloc(unsigned int n) { void *x; if (n == 0) error("%s: asked to allocate zero bytes", __func__); x = malloc(n); if (!x) error("%s: couldn't get %d bytes", __func__, n); return x; } /* * Read until a certain string is found. Only look for matches appearing * somewhere after inptr. Start reading to the end of the string if inptr * != 0. Always terminates strings with null. */ int read_until(int fd, char *buf, size_t count, const char *until, int inptr) { int cc, ptr; if (count == 0) { if (*until == 0) return 0; else error("%s: size 0 buf no until string found", __func__); } if (inptr) ptr = strlen(buf); else { ptr = 0; buf[ptr] = '\0'; } for (;;) { if (strstr(buf+inptr, until)) return ptr; cc = read(fd, buf + ptr, 1); /* painfully slow, but easy to code */ if (cc <= 0) return cc; ptr += cc; buf[ptr] = '\0'; } } /* * Gcc >= "2.96" seem to have this nice addition. 2.95.2 does not. */ #ifdef __GNUC__ # if __GNUC__ >= 3 # define USE_FORMAT_SIZE_T 1 # endif # if __GNUC__ == 2 && __GNUC_MINOR__ >= 96 # define USE_FORMAT_SIZE_T 1 # endif #endif #ifndef USE_FORMAT_SIZE_T # define USE_FORMAT_SIZE_T 0 #endif /* * Loop over reading until everything arrives. Error if not. */ void read_full(int fd, void *buf, size_t num) { int cc, offset = 0; int total = num; while (num > 0) { cc = read(fd, (char *)buf + offset, num); if (cc < 0) { if (USE_FORMAT_SIZE_T) error_errno("%s: read %zu bytes", __func__, num); else error_errno("%s: read %lu bytes", __func__, (long unsigned int) num); } if (cc == 0) error("%s: EOF, only %d of %d bytes", __func__, offset, total); num -= cc; offset += cc; } } /* * Loop over reading until everything arrives. Return numbytes read, * just like read, or 0 for eof, or negative for some error. Eventually * get rid of the non_ret version and check errors everywhere. */ int read_full_ret(int fd, void *buf, size_t num) { int cc, offset = 0; int total = num; while (num > 0) { cc = read(fd, (char *)buf + offset, num); if (cc <= 0) return cc; num -= cc; offset += cc; } return total; } /* * Keep looping until all bytes have been accepted by the kernel. Return * count if all okay, else -1 with errno set. */ int write_full(int fd, const void *buf, size_t count) { int cc, ptr = 0; int total = count; while (count > 0) { cc = write(fd, (const char *)buf + ptr, count); if (cc < 0) return cc; count -= cc; ptr += cc; } return total; } /* * Write the length, then the bytes, of a null-terminated string. */ int write_full_string(int fd, const char *s) { int len; int ret; len = strlen(s); ret = write_full(fd, &len, sizeof(len)); if (ret < 0) return ret; ret = write_full(fd, s, len+1); return ret; } /* * Read and allocate a string into a given pointer. */ int read_full_string(int fd, char **s) { int len; int ret; ret = read_full_ret(fd, &len, sizeof(len)); if (ret < 0) return ret; *s = Malloc((len+1)); ret = read_full_ret(fd, *s, len+1); return ret; }