/*
* util.c - general utilities
*
* $Id: util.c 361 2006-04-19 19:32:22Z pw $
*
* Copyright (C) 2000-6 Pete Wyckoff <pw@osc.edu>
*
* Distributed under the GNU Public License Version 2 or later (See LICENSE)
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <sys/time.h>
#include <pbs_error.h> /* 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; i<num_tm_errs; i++)
if (err == tm_errs[i].err)
return tm_errs[i].str;
return NULL;
}
static void
error_tm_print(int err)
{
const char *s;
fprintf(stderr, ": tm: ");
s = tm_errno_to_str(err);
if (s)
fprintf(stderr, "%s.\n", s);
else
fprintf(stderr, "unknown error %d.\n", err);
}
/*
* Debug message.
*/
void
debug(int level, const char *fmt, ...)
{
va_list ap;
if (cl_args->verbose >= 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;
}
syntax highlighted by Code2HTML, v. 0.9.1