/*
* Amanda, The Advanced Maryland Automatic Network Disk Archiver
* Copyright (c) 1997-1998 University of Maryland at College Park
* Copyright (c) 2007-2012 Zmanda, Inc All Rights Reserved.
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of U.M. not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. U.M. makes no representations about the
* suitability of this software for any purpose. It is provided "as is"
* without express or implied warranty.
*
* U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
* BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Author: AMANDA core development group.
*/
/*
* $Id: file.c,v 1.40 2006/07/19 17:41:15 martinea Exp $
*
* file and directory bashing routines
*/
#include "amanda.h"
#include "amutil.h"
#include "timestamp.h"
#include "file.h"
static struct areads_buffer *areads_getbuf(const char *s, int l, int fd);
static char *original_cwd = NULL;
/*
* Make a directory hierarchy given an entry to be created (by the caller)
* in the new target. In other words, create all the directories down to
* the last element, but not the last element. So a (potential) file name
* may be passed to mkpdir and all the parents of that file will be created.
*/
int
mkpdir(
char * file, /* file to create parent directories for */
mode_t mode, /* mode for new directories */
uid_t uid, /* uid for new directories */
gid_t gid) /* gid for new directories */
{
char *dir;
char *p;
int rc; /* return code */
rc = 0;
/* Remove last member of file, put the result in dir */
dir = g_strdup(file); /* make a copy we can play with */
p = strrchr(dir, '/');
if (p)
*p = '\0';
rc = mkdir(dir, mode);
if (rc != 0) {
if (errno == ENOENT) { /* create parent directory */
rc = mkpdir(dir, mode, uid, gid);
if (rc != 0) {
amfree(dir);
return rc;
}
rc = mkdir(dir, mode);
}
if (rc != 0 && errno == EEXIST) {
amfree(dir);
return 0;
}
}
/* mkdir succeded, set permission and ownership */
if (rc == 0) {
/* mkdir is affected by umask, so set the mode bits manually */
rc = chmod(dir, mode);
if (rc == 0 && geteuid() == 0) {
rc = chown(dir, uid, gid);
}
}
amfree(dir);
return rc;
}
/* Remove as much of a directory hierarchy as possible.
** Notes:
** - assumes that rmdir() on a non-empty directory will fail!
** - stops deleting before topdir, ie: topdir will not be removed
** - if file is not under topdir this routine will not notice
*/
int
rmpdir(
char * file, /* directory hierarchy to remove */
char * topdir) /* where to stop removing */
{
int rc;
char *p, *dir;
if(g_str_equal(file, topdir)) return 0; /* all done */
rc = rmdir(file);
if (rc != 0) switch(errno) {
#ifdef ENOTEMPTY
#if ENOTEMPTY != EEXIST /* AIX makes these the same */
case ENOTEMPTY:
#endif
#endif
case EEXIST: /* directory not empty */
return 0; /* cant do much more */
case ENOENT: /* it has already gone */
rc = 0; /* ignore */
break;
case ENOTDIR: /* it was a file */
rc = unlink(file);
break;
}
if(rc != 0) return -1; /* unexpected error */
dir = g_strdup(file);
p = strrchr(dir, '/');
if (p == NULL || p == dir) {
rc = 0;
} else {
*p = '\0';
rc = rmpdir(dir, topdir);
}
amfree(dir);
return rc;
}
/*
*=====================================================================
* Change directory to a "safe" location and set some base environment.
*
* void safe_cd (void)
*
* Set a default umask of 0077.
*
* Create the Amada debug directory (if defined) and the Amanda temp
* directory.
*
* Try to chdir to the Amanda debug directory first, but it must be owned
* by the Amanda user and not allow rwx to group or other. Otherwise,
* try the same thing to the Amanda temp directory.
*
* If that is all OK, call save_core().
*
* Otherwise, cd to "/" so if we take a signal we cannot drop core
* unless the system administrator has made special arrangements (e.g.
* pre-created a core file with the right ownership and permissions).
*=====================================================================
*/
void
safe_cd(void)
{
int cd_ok = 0;
struct stat sbuf;
char *d;
uid_t client_uid = get_client_uid();
gid_t client_gid = get_client_gid();
(void) umask(0077);
/* stash away the current directory for later reference */
if (original_cwd == NULL) {
original_cwd = g_get_current_dir();
}
if (client_uid != (uid_t) -1) {
#if defined(AMANDA_DBGDIR)
d = g_strconcat(AMANDA_DBGDIR, "/.", NULL);
(void) mkpdir(d, (mode_t)0700, client_uid, client_gid);
amfree(d);
#endif
d = g_strconcat(AMANDA_TMPDIR, "/.", NULL);
(void) mkpdir(d, (mode_t)0700, client_uid, client_gid);
amfree(d);
}
#if defined(AMANDA_DBGDIR)
if (chdir(AMANDA_DBGDIR) != -1
&& stat(".", &sbuf) != -1
&& (sbuf.st_mode & 0777) == 0700 /* drwx------ */
&& sbuf.st_uid == client_uid) { /* owned by Amanda user */
cd_ok = 1; /* this is a good place to be */
}
#endif
if (! cd_ok
&& chdir(AMANDA_TMPDIR) != -1
&& stat(".", &sbuf) != -1
&& (sbuf.st_mode & 0777) == 0700 /* drwx------ */
&& sbuf.st_uid == client_uid) { /* owned by Amanda user */
cd_ok = 1; /* this is a good place to be */
}
if(cd_ok) {
save_core(); /* save any old core file */
} else {
if ((cd_ok = chdir("/")) == -1) {
(void)cd_ok; /* Quiet compiler warning if DEBUG disabled */
}
}
}
/*
*=====================================================================
* Close all file descriptors except stdin, stdout and stderr. Make
* sure they are open.
*
* void safe_fd (fd_start, fd_count)
*
* entry: fd_start - start of fd-s to leave alone (or -1)
* fd_count - count of fd-s to leave alone
* exit: none
*
* On exit, all three standard file descriptors will be open and pointing
* someplace (either what we were handed or /dev/null) and all other
* file descriptors (up to FD_SETSIZE) will be closed.
*=====================================================================
*/
void
safe_fd(
int fd_start,
int fd_count)
{
safe_fd3(fd_start, fd_count, 0, 0);
}
/*
*=====================================================================
* Close all file descriptors except stdin, stdout and stderr. Make
* sure they are open.
*
* void safe_fd2 (fd_start, fd_count, fd1)
*
* entry: fd_start - start of fd-s to leave alone (or -1)
* fd_count - count of fd-s to leave alone
* fd1 - do not close
* exit: none
*
* On exit, all three standard file descriptors will be open and pointing
* someplace (either what we were handed or /dev/null) and all other
* file descriptors (up to FD_SETSIZE) will be closed.
*=====================================================================
*/
void
safe_fd2(
int fd_start,
int fd_count,
int fd1)
{
safe_fd3(fd_start, fd_count, fd1, 0);
}
/*
*=====================================================================
* Close all file descriptors except stdin, stdout and stderr. Make
* sure they are open.
*
* void safe_fd3 (fd_start, fd_count, fd1, fd2)
*
* entry: fd_start - start of fd-s to leave alone (or -1)
* fd_count - count of fd-s to leave alone
* fd1 - do not close
* fd2 - do not close
* exit: none
*
* On exit, all three standard file descriptors will be open and pointing
* someplace (either what we were handed or /dev/null) and all other
* file descriptors (up to FD_SETSIZE) will be closed.
*=====================================================================
*/
void
safe_fd3(
int fd_start,
int fd_count,
int fd1,
int fd2)
{
int fd;
for(fd = 0; fd < (int)FD_SETSIZE; fd++) {
if (fd < 3) {
/*
* Open three file descriptors. If one of the standard
* descriptors is not open it will be pointed to /dev/null...
*
* This avoids, for instance, someone running us with stderr
* closed so that when we open some other file, messages
* sent to stderr do not accidentally get written to the
* wrong file.
*/
if (fcntl(fd, F_GETFD) == -1) {
if (open("/dev/null", O_RDWR) == -1) {
g_fprintf(stderr, _("/dev/null is inaccessable: %s\n"),
strerror(errno));
exit(1);
}
}
} else {
/*
* Make sure nobody spoofs us with a lot of extra open files
* that would cause an open we do to get a very high file
* descriptor, which in turn might be used as an index into
* an array (e.g. an fd_set).
*/
if ((fd < fd_start || fd >= fd_start + fd_count) &&
(fd != fd1) &&
(fd != fd2)) {
close(fd);
}
}
}
}
/*
*=====================================================================
* Save an existing core file.
*
* void save_core (void)
*
* entry: none
* exit: none
*
* Renames:
*
* "core" to "coreYYYYMMDD",
* "coreYYYYMMDD" to "coreYYYYMMDDa",
* "coreYYYYMMDDa" to "coreYYYYMMDDb",
* ...
*
* ... where YYYYMMDD is the modification time of the original file.
* If it gets that far, an old "coreYYYYMMDDz" is thrown away.
*=====================================================================
*/
void
save_core(void)
{
struct stat sbuf;
if(stat("core", &sbuf) != -1) {
char *ts;
char suffix[2];
char *old, *new;
ts = get_datestamp_from_time(sbuf.st_mtime);
suffix[0] = 'z';
suffix[1] = '\0';
old = g_strjoin(NULL, "core", ts, suffix, NULL);
new = NULL;
while(ts[0] != '\0') {
amfree(new);
new = old;
if(suffix[0] == 'a') {
suffix[0] = '\0';
} else if(suffix[0] == '\0') {
ts[0] = '\0';
} else {
suffix[0]--;
}
old = g_strjoin(NULL, "core", ts, suffix, NULL);
(void)rename(old, new); /* it either works ... */
}
amfree(ts);
amfree(old);
amfree(new);
}
}
/*
** Sanitise a file name.
**
** Convert all '/', ':', and '\' characters to '_' so that we can use,
** for example, disk names as part of file names.
** Notes:
** - there is a many-to-one mapping between input and output
** - Only / and '\0' are disallowed in filenames by POSIX, but Windows
** disallows ':' and '\' as well. Furthermore, we use ':' as a
** delimiter at other points in Amanda.
*/
char *
sanitise_filename(
char * inp)
{
char *ret = g_strdup(inp);
return g_strdelimit(ret, "/:\\", '_');
}
/* duplicate '_' */
char *
old_sanitise_filename(
char * inp)
{
char *buf;
size_t buf_size;
char *s, *d;
int ch;
buf_size = 2*strlen(inp) + 1; /* worst case */
buf = g_malloc(buf_size);
d = buf;
s = inp;
while((ch = *s++) != '\0') {
if(ch == '_') {
*d++ = (char)ch;
}
if(ch == '/') {
ch = '_'; /* convert "bad" to "_" */
}
*d++ = (char)ch;
}
assert(d < buf + buf_size);
*d = '\0';
return buf;
}
void
canonicalize_pathname(char *pathname, char *result_buf)
{
#ifdef __CYGWIN__
cygwin_conv_to_full_posix_path(pathname, result_buf);
#else
strncpy(result_buf, pathname, PATH_MAX-1);
result_buf[PATH_MAX-1] = '\0';
#endif
}
/*
*=====================================================================
* Get the next line of input from a stdio file.
*
* char *agets (FILE *stream)
*
* entry: stream - stream to read
* exit: returns a pointer to an g_malloc'd string or NULL
* at EOF or error. The functions ferror(stream) and
* feof(stream) should be checked by caller to determine
* stream status.
*
* Notes: the newline at the end of a line, if read, is removed from
* the string. Quoted newlines are left intact.
* the caller is responsible for free'ing the string
*
*=====================================================================
*/
#define AGETS_LINE_INCR 128
char *
debug_agets(
const char *sourcefile,
int lineno,
FILE * stream)
{
int ch;
char *line = g_malloc(AGETS_LINE_INCR);
size_t line_size = 0;
size_t loffset = 0;
int inquote = 0;
int escape = 0;
(void)sourcefile; /* Quiet unused parameter warning if not debugging */
(void)lineno; /* Quiet unused parameter warning if not debugging */
while ((ch = fgetc(stream)) != EOF) {
if (ch == '#' && !escape && !inquote) {
// consume to the end of line.
ch = fgetc(stream);
while (ch != EOF && ch != '\n') {
ch = fgetc(stream);
}
break;
}
if (ch == '\n') {
if (!inquote) {
if (escape) {
escape = 0;
loffset--; /* Consume escape in buffer */
continue;
}
/* Reached end of line so exit without passing on LF */
break;
}
}
if (ch == '\\') {
escape = !escape;
} else {
if (ch == '"') {
if (!escape)
inquote = !inquote;
}
escape = 0;
}
if ((loffset + 1) >= line_size) {
char *tmpline;
/*
* Reallocate input line.
* g_malloc() never return NULL pointer.
*/
tmpline = g_malloc(line_size + AGETS_LINE_INCR);
memcpy(tmpline, line, line_size);
amfree(line);
line = tmpline;
line_size = line_size + AGETS_LINE_INCR;
}
line[loffset++] = (char)ch;
}
if ((ch == EOF) && (loffset == 0)) {
amfree(line); /* amfree zeros line... */
} else {
line[loffset] = '\0';
}
/*
* Return what we got even if there was not a newline.
* Only report done (NULL) when no data was processed.
*/
return line;
}
char *
debug_pgets(
const char *sourcefile,
int lineno,
FILE * stream)
{
char *line = g_malloc(AGETS_LINE_INCR);
char *l;
char *cline;
char *untainted_line;
char *ul;
size_t line_size = AGETS_LINE_INCR;
size_t loffset = 0;
(void)sourcefile; /* Quiet unused parameter warning if not debugging */
(void)lineno; /* Quiet unused parameter warning if not debugging */
line[0] = '\0';
cline = fgets(line, line_size, stream);
if (!cline) {
g_free(line);
return cline;
}
loffset = strlen(line);
while (cline && loffset == line_size-1 && line[loffset-1] != '\n') {
char *tmpline;
char *pline;
line_size *= 2;
tmpline = g_malloc(line_size);
memcpy(tmpline, line, loffset+1);
amfree(line);
line = tmpline;
pline = line + loffset;
cline = fgets(pline, line_size-loffset, stream);
loffset += strlen(pline);
}
if (line[loffset-1] == '\n')
line[loffset-1] = '\0';
untainted_line = ul = g_malloc(loffset+1);
for (l = line; *l != '\0'; l++) {
*ul++ = *l;
}
*ul = '\0';
g_free(line);
return untainted_line;
}
/*
*=====================================================================
* Find/create a buffer for a particular file descriptor for use with
* areads().
*
* void areads_getbuf (const char *file, size_t line, int fd)
*
* entry: file, line = caller source location
* fd = file descriptor to look up
* exit: returns a pointer to the buffer, possibly new
*=====================================================================
*/
GMutex *file_mutex = NULL;
static struct areads_buffer {
char *buffer;
char *endptr;
size_t bufsize;
} **areads_buffer = NULL;
static int areads_bufcount = 0;
static size_t areads_bufsize = BUFSIZ; /* for the test program */
static struct areads_buffer *
areads_getbuf(
const char *s G_GNUC_UNUSED,
int l G_GNUC_UNUSED,
int fd)
{
struct areads_buffer *ptr;
assert(fd >= 0);
g_mutex_lock(file_mutex);
if (fd >= areads_bufcount) {
struct areads_buffer **new;
int afd = 30;
int i;
if (afd < fd * 2)
afd = fd * 2;
new = g_new0(struct areads_buffer *, (size_t)afd);
if (areads_buffer) {
size_t size = areads_bufcount * sizeof(*areads_buffer);
memcpy(new, areads_buffer, size);
}
for (i = areads_bufcount; i < afd; i++){
new[i] = g_new0(struct areads_buffer, 1);
}
amfree(areads_buffer);
areads_buffer = new;
areads_bufcount = afd;
}
ptr = areads_buffer[fd];
g_mutex_unlock(file_mutex);
if (ptr->buffer == NULL) {
ptr->bufsize = areads_bufsize;
ptr->buffer = g_malloc(ptr->bufsize + 1);
ptr->buffer[0] = '\0';
ptr->endptr = ptr->buffer;
}
return ptr;
}
/*
*=====================================================================
* Return the amount of data still in an areads buffer.
*
* ssize_t areads_dataready (int fd)
*
* entry: fd = file descriptor to release buffer for
* exit: returns number of bytes of data ready to process
*=====================================================================
*/
ssize_t
areads_dataready(
int fd)
{
ssize_t r = 0;
SELECT_ARG_TYPE ready;
struct timeval to;
int nfound;
if (fd < 0)
return 0;
g_mutex_lock(file_mutex);
if (fd >= 0 && fd < areads_bufcount && areads_buffer[fd]->buffer != NULL) {
r = (ssize_t) (areads_buffer[fd]->endptr - areads_buffer[fd]->buffer);
}
g_mutex_unlock(file_mutex);
if (r) {
return r;
}
FD_ZERO(&ready);
FD_SET(fd, &ready);
to.tv_sec = 0;
to.tv_usec = 0;
nfound = select(fd+1, &ready, NULL, NULL, &to);
if (nfound > 0 && FD_ISSET(fd, &ready)) {
return 1;
} else {
return 0;
}
}
/*
*=====================================================================
* Release a buffer for a particular file descriptor used by areads().
*
* void areads_relbuf (int fd)
*
* entry: fd = file descriptor to release buffer for
* exit: none
*=====================================================================
*/
void
areads_relbuf(
int fd)
{
g_mutex_lock(file_mutex);
if(fd >= 0 && fd < areads_bufcount) {
amfree(areads_buffer[fd]->buffer);
areads_buffer[fd]->endptr = NULL;
areads_buffer[fd]->bufsize = 0;
}
g_mutex_unlock(file_mutex);
}
/*
*=====================================================================
* Get the next line of input from a file descriptor.
*
* char *areads (int fd)
*
* entry: fd = file descriptor to read
* exit: returns a pointer to an g_malloc'd string or NULL at EOF
* or error (errno will be zero on EOF).
*
* Notes: the newline, if read, is removed from the string
* the caller is responsible for free'ing the string
*=====================================================================
*/
char *
debug_areads (
const char *s,
int l,
int fd)
{
char *nl;
char *line;
char *buffer;
char *endptr;
char *newbuf;
size_t buflen;
size_t size;
ssize_t r;
struct areads_buffer *ptr;
if(fd < 0) {
errno = EBADF;
return NULL;
}
ptr = areads_getbuf(s, l, fd);
buffer = ptr->buffer;
endptr = ptr->endptr;
buflen = ptr->bufsize - (size_t)(endptr - buffer);
while((nl = strchr(buffer, '\n')) == NULL) {
/*
* No newline yet, so get more data.
*/
if (buflen == 0) {
if ((size = ptr->bufsize) < 256 * areads_bufsize) {
size *= 2;
} else {
size += 256 * areads_bufsize;
}
newbuf = g_malloc(size + 1);
memcpy (newbuf, buffer, ptr->bufsize + 1);
amfree(ptr->buffer);
ptr->buffer = newbuf;
ptr->endptr = newbuf + ptr->bufsize;
ptr->bufsize = size;
buffer = ptr->buffer;
endptr = ptr->endptr;
buflen = ptr->bufsize - (size_t)(endptr - buffer);
}
if ((r = read(fd, endptr, buflen)) <= 0) {
if(r == 0) {
if (buffer == endptr || *(endptr-1) == '\n') {
errno = 0; /* flag EOF instead of error */
return NULL;
}
*endptr = '\n';
r = 1;
} else {
return NULL;
}
} else {
endptr[r] = '\0'; /* we always leave room for this */
endptr += r;
buflen -= r;
}
}
*nl++ = '\0';
line = g_strdup(buffer);
size = (size_t)(endptr - nl); /* data still left in buffer */
memmove(buffer, nl, size);
ptr->endptr = buffer + size;
ptr->endptr[0] = '\0';
return line;
}
int robust_open(const char * pathname, int flags, mode_t mode) {
int result = -1;
int e_busy_count = 0;
for (;;) {
if (flags & O_CREAT) {
result = open(pathname, flags, mode);
} else {
result = open(pathname, flags);
}
if (result < 0) {
#ifdef EBUSY
/* EBUSY is a tricky one; sometimes it is synonymous with
EINTR, but sometimes it means the device is open
elsewhere (e.g., with a tape drive on Linux). We take
the middle path and retry, but with limited
patience. */
if (errno == EBUSY && e_busy_count < 10) {
e_busy_count ++;
continue;
} else
#endif
if (0
/* Always retry on EINTR; if the caller did
not specify non-blocking mode, then also retry on
EAGAIN or EWOULDBLOCK. */
#ifdef EINTR
|| errno == EINTR
#endif
|| ( 1
#ifdef O_NONBLOCK
&& !(flags & O_NONBLOCK)
#endif
&& ( 0
#ifdef EAGAIN
|| errno == EAGAIN
#endif
#ifdef EWOULDBLOCK
|| errno == EWOULDBLOCK
#endif
) ) ) {
/* Try again */
continue;
} else {
/* Failure. */
return result;
}
} else {
break;
}
}
#ifdef F_SETFD
if (result >= 0) {
(void)fcntl(result, F_SETFD, 1); /* Throw away result. */
}
#endif
return result;
}
int robust_close(int fd) {
for (;;) {
int result;
result = close(fd);
if (result != 0 && (0
#ifdef EINTR
|| errno == EINTR
#endif
#ifdef EBUSY
|| errno == EBUSY
#endif
#ifdef EAGAIN
|| errno == EAGAIN
#endif
#ifdef EWOULDBLOCK
|| errno == EWOULDBLOCK
#endif
)) {
continue;
} else {
return result;
}
}
}
uid_t
get_client_uid(void)
{
static uid_t client_uid = (uid_t) -1;
struct passwd *pwent;
if(client_uid == (uid_t) -1 && (pwent = getpwnam(CLIENT_LOGIN)) != NULL) {
client_uid = pwent->pw_uid;
endpwent();
}
return client_uid;
}
gid_t
get_client_gid(void)
{
static gid_t client_gid = (gid_t) -1;
struct passwd *pwent;
if(client_gid == (gid_t) -1 && (pwent = getpwnam(CLIENT_LOGIN)) != NULL) {
client_gid = pwent->pw_gid;
endpwent();
}
return client_gid;
}
char *
get_original_cwd(void)
{
if (original_cwd == NULL) {
original_cwd = g_get_current_dir();
}
return original_cwd;
}
/**
* Read up to "count" bytes from a file descriptor, optionally collecting the
* read operation status (0 on success, not 0 otherwise).
*
* This function exists to overcome the confusing behavior of full_read():
*
* - unlike read(2), full_read() does not return -1 on failure;
* - errno needs to be explicitly checked for each time the number of bytes
* actually read is less than the "count" argument.
*
* With this function, a full read becomes a one time process. Error collecting
* is optional.
*
* @param fd: the file descriptor to read from
* @param buf: the buffer to write data into
* @param count: the number of bytes to write into the buffer
* @param err (output): 0 if "count" bytes have been read; errno (as set by
* full_read()) otherwise
* @returns: the number of bytes read from the file descriptor
*/
gsize read_fully(int fd, void *buf, gsize count, int *err)
{
gsize ret = full_read(fd, buf, count);
if (err)
*err = (ret == count) ? 0 : errno;
return ret;
}
char *
untaint_fgets(
char *s,
int size,
FILE *stream)
{
char *untainted_line = malloc(size);
char *line = fgets(untainted_line, size, stream);
char *s1 = s;
if (!line) {
g_free(untainted_line);
return NULL;
}
while (*line != '\0') {
*s1 = *line;
line++;
s1++;
}
*s1 = '\0';
g_free(untainted_line);
return s;
}
#ifdef TEST
int
main(
int argc,
char ** argv)
{
int rc;
int fd;
char *name;
char *top;
char *file;
char *line;
glib_init();
/*
* Configure program for internationalization:
* 1) Only set the message locale for now.
* 2) Set textdomain for all amanda related programs to "amanda"
* We don't want to be forced to support dozens of message catalogs
*/
setlocale(LC_MESSAGES, "C");
textdomain("amanda");
safe_fd(-1, 0);
set_pname("file test");
dbopen(NULL);
/* Don't die when child closes pipe */
signal(SIGPIPE, SIG_IGN);
name = "/tmp/a/b/c/d/e";
if (argc > 2 && argv[1][0] != '\0') {
name = argv[1];
}
top = "/tmp";
if (argc > 3 && argv[2][0] != '\0') {
name = argv[2];
}
file = "/etc/hosts";
if (argc > 4 && argv[3][0] != '\0') {
name = argv[3];
}
g_fprintf(stderr, _("Create parent directories of %s ..."), name);
rc = mkpdir(name, (mode_t)02777, (uid_t)-1, (gid_t)-1);
if (rc == 0)
g_fprintf(stderr, " done\n");
else {
perror(_("failed"));
return rc;
}
g_fprintf(stderr, _("Delete %s back to %s ..."), name, top);
rc = rmpdir(name, top);
if (rc == 0)
g_fprintf(stderr, _(" done\n"));
else {
perror(_("failed"));
return rc;
}
g_fprintf(stderr, _("areads dump of %s ..."), file);
if ((fd = open (file, 0)) < 0) {
perror(file);
return 1;
}
areads_bufsize = 1; /* force buffer overflow */
while ((line = areads(fd)) != NULL) {
puts(line);
amfree(line);
}
aclose(fd);
g_fprintf(stderr, _(" done.\n"));
g_fprintf(stderr, _("Finished.\n"));
dbclose();
return 0;
}
#endif