CAP/applications/aufs/afpidsrvr.c

831 lines
15 KiB
C

/*
* $Author: djh $ $Date: 1996/06/19 10:32:13 $
* $Header: /mac/src/cap60/applications/aufs/RCS/afpidsrvr.c,v 2.5 1996/06/19 10:32:13 djh Rel djh $
* $Revision: 2.5 $
*
*/
/*
* Server to provide AUFS fixed directory ID database write facilities
*
* John Forrest <jf@ap.co.umist.ac.uk>
*
*/
#ifdef FIXED_DIRIDS
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/param.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h>
#include <stdio.h>
#include <signal.h>
#include <fcntl.h>
#ifdef USE_GDBM
#include <gdbm.h>
#else /* USE_GDBM */
#include <ndbm.h>
#endif /* USE_GDBM */
#include <assert.h>
#ifdef USESTRINGDOTH
# include <string.h>
#else USESTRINGDOTH
# include <strings.h>
#endif USESTRINGDOTH
#include "../../lib/afp/afpidaufs.h"
int verbose = 0;
int disconnect = 1;
char *log = NULL;
int continue_clean;
extern char *optarg;
extern int optind, opterr;
int queries; /* request socket */
int bound_queries = 0; /* whether to delete socket or not */
void create_queries();
void session();
void set_signal();
void do_disconnect();
void clean_entries();
void open_database();
void
fatal(message)
char *message;
{
if (message != NULL)
fprintf(stderr, "%s:", message);
if (errno > 0) {
fprintf(stderr, " %d: ", errno);
perror("");
} else
putc('\n', stderr);
#ifdef USE_GDBM
if (db != NULL)
gdbm_close(db);
#else /* USE_GDBM */
if (db != NULL)
dbm_close(db);
#endif /* USE_GDBM */
if (bound_queries)
unlink(aufsSockname);
exit(-1);
}
void
fatal2(message, a1)
char *message, *a1;
{
fprintf(stderr, "afpidsrvr: fatal error: %s%s :", message, a1);
fatal(NULL);
}
void
clean_exit(n)
int n;
{
#ifdef USE_GDBM
if (db != NULL)
gdbm_close(db);
#else /* USE_GDBM */
if (db != NULL)
dbm_close(db);
#endif /* USE_GDBM */
if (bound_queries)
unlink(aufsSockname);
exit(n);
}
main(argc, argv)
char *argv[];
{
int c;
extern char *optarg;
int doClean = 0;
while ((c = getopt(argc, argv, "vtcl:")) != -1) {
switch (c) {
case 'v':
verbose = 1;
break;
case 't':
disconnect = 0;
break;
case 'l':
log = optarg;
break;
case 'c':
doClean = 1;
break;
case '?':
fprintf(stderr,
"usage: afpidsrvr [-c] [-v] [-t] [-l logfile]\n");
exit(-1);
}
}
set_signal();
if (disconnect)
do_disconnect();
if (log) {
int fd;
if ((fd = open(log, O_WRONLY|O_APPEND|O_CREAT)) < 0)
fd = open("/dev/null", O_WRONLY);
if (fd >= 0) {
#ifndef NODUP2
dup2(fd, 2);
#else NODUP2
close(2);
dup(fd);
#endif NODUP2
close(fd);
}
}
init_aufsExt();
open_database();
create_queries();
if (doClean)
do
clean_entries();
while (continue_clean);
#ifdef USE_GDBM
gdbm_close(db);
db = NULL;
#endif /* USE_GDBM */
{
time_t now;
time(&now);
fprintf(stderr, "Starting afpidsrvr with %s at %s",
aufsDbName, ctime(&now));
}
session();
/* NOTREACHED */
}
/*
* create queries "pipe" entry
*
*/
void
create_queries()
{
struct sockaddr *addr;
int addrlen;
if ((queries = query_socket()) < 0)
fatal("Problem creating socket");
query_addr(&addr, &addrlen);
if (bind(queries, addr, addrlen) < 0)
fatal2("Problem binding socket ", aufsSockname);
bound_queries = 1;
#ifdef linux
chmod(aufsSockname, 0666);
#endif /* linux */
if (listen(queries, 5) < 0)
fatal("Listen");
return;
}
void
do_exit()
{
clean_exit(-1);
}
/*
* force clean exit
*
*/
void
set_signal()
{
if (signal(SIGHUP, SIG_IGN)!=SIG_IGN)
signal(SIGHUP, do_exit);
if (signal(SIGINT, SIG_IGN)!=SIG_IGN)
signal(SIGINT, do_exit);
if (signal(SIGTERM, SIG_IGN)!=SIG_IGN)
signal(SIGTERM, do_exit);
}
void
create_database()
{
datum initial_key, initial;
datum root, root_datum;
#ifdef USE_GDBM
if ((db = gdbm_open(aufsDbName, 2048, GDBM_WRCREAT, 0644, 0L)) == NULL)
fatal("Creating db");
#else /* USE_GDBM */
if ((db = dbm_open(aufsDbName, O_RDWR|O_CREAT, 0644)) == NULL)
fatal("Creating db");
#endif /* USE_GDBM */
rootEid = valid_id(); /* any will do */
root_datum = new_entry(aufsRootName, (sdword)0);
root = num_datum(rootEid);
#ifdef USE_GDBM
if (gdbm_store(db, root, root_datum, GDBM_REPLACE) < 0)
fatal("Writing root record");
#else /* USE_GDBM */
if (dbm_store(db, root, root_datum, DBM_REPLACE) < 0)
fatal("Writing root record");
#endif /* USE_GDBM */
initial_key.dptr = "I";
initial_key.dsize = strlen(initial_key.dptr);
initial = create_init(aufsDbVersion, rootEid);
#ifdef USE_GDBM
if (gdbm_store(db, initial_key, initial, GDBM_REPLACE) < 0)
fatal("Writing root record");
#else /* USE_GDBM */
if (dbm_store(db, initial_key, initial, DBM_REPLACE) < 0)
fatal("Writing root record");
#endif /* USE_GDBM */
flush_database();
}
void
open_database()
{
datum initial_key, initial;
char *version;
initial_key.dptr = "I";
initial_key.dsize = strlen(initial_key.dptr);
#ifdef USE_GDBM
db = gdbm_open(aufsDbName, 2048, GDBM_READER, 0644, 0L);
#else /* USE_GDBM */
db = dbm_open(aufsDbName, O_RDWR, 0644);
#endif /* USE_GDBM */
if (db == NULL && errno == ENOENT)
create_database();
if (db == NULL)
fatal("Opening db");
#ifdef USE_GDBM
initial = gdbm_fetch(db, initial_key);
#else /* USE_GDBM */
initial = dbm_fetch(db, initial_key);
#endif /* USE_GDBM */
if (initial.dptr == NULL)
fatal("Suspect Database");
if (extract_init(initial, &version, &rootEid) < 0)
fatal("Problem with initial record");
if (strcmp(version, aufsDbVersion) != 0)
fatal2("Incompatible d/b, can't deal with version ", version);
#ifdef USE_GDBM
free(initial.dptr);
#endif /* USE_GDBM */
return;
}
void
add_entry(entry)
char *entry;
{
int ret;
if (!is_directory(entry))
return;
if ((ret = lookup_path(entry, NULL, NULL, 1)) <= 0)
fprintf(stderr, "Failed to create '%s' (%d, %d)\n",
entry, ret, errno);
return;
}
void
delete_entry_id();
void
add_entry_id(parent, rest)
sdword parent;
char *rest;
{
int ret;
sdword id;
char *entry;
if ((ret = lookup_path_id(parent, rest, &id, NULL, 1)) <= 0) {
fprintf(stderr, "Failed to create %d/'%s' (%d, %d)\n",
parent, rest, ret, errno);
return;
}
if (verbose)
fprintf(stderr, "Created entry %d/'%s' (%d, %d) id = %d\n",
parent, rest, ret, errno, id);
entry = equiv_path(id);
if (!is_directory(entry)) /* check existance in restrospect! */
delete_entry_id(id);
flush_database();
return;
}
void
add_entry_fid(parent, rest)
sdword parent;
char *rest;
{
int ret;
sdword id;
char *entry;
if ((ret = lookup_path_id(parent, rest, &id, NULL, 1)) <= 0) {
fprintf(stderr, "Failed to create %d/'%s' (%d, %d)\n",
parent, rest, ret, errno);
return;
}
entry = equiv_path(id);
if (!is_file(entry)) /* check existance in restrospect! */
delete_entry_id(id);
flush_database();
return;
}
void
delete_entry(entry)
char *entry;
{
sdword id, parent;
datum data;
if (is_directory(entry) || is_file(entry))
return;
if (lookup_path(entry, &id, &data, 0) > 0){
(void) extract_entry(data, NULL, &parent, NULL, NULL);
do_delete_entry(id);
delete_child(parent, id);
flush_database();
}
return;
}
void
delete_entry_id(id)
sdword id;
{
sdword parent;
datum data;
char *entry = equiv_path(id);
if (is_directory(entry) || is_file(entry))
return;
data = get_datum(id);
if (data.dptr == NULL)
return;
if (extract_entry(data, NULL, &parent, NULL, NULL) < 0)
return;
do_delete_entry(id);
delete_child(parent, id);
flush_database();
return;
}
void
delete_entry_fid(id)
sdword id;
{
sdword parent;
datum data;
char *entry = equiv_path(id);
if (is_file(entry))
return;
data = get_datum(id);
if (data.dptr == NULL)
return;
if (extract_entry(data, NULL, &parent, NULL, NULL) < 0)
return;
do_delete_entry(id);
delete_child(parent, id);
flush_database();
return;
}
void
move_entry(from, to)
char *from, *to;
{
datum data;
sdword id, from_parent, to_parent;
char to_directory[MAXPATHLEN];
char *new_name;
char *name;
char *ptr;
assert(to[0] == '/');
if (is_directory(from) || !is_directory(to))
if (is_file(from) || !is_file(to))
return;
if (lookup_path(from, &id, &data, 0) <= 0) {
/* did not know previously! */
add_entry(to);
return;
}
if (lookup_path(to, NULL, NULL, 0) > 0) /* exists already! */
return;
extract_entry(data, &name, &from_parent, NULL, NULL);
name = string_copy(name); /* just in case it gets clobbered */
ptr = rindex(to, '/');
strcpy(to_directory, "");
strncat(to_directory, to, ptr-to);
new_name = ptr+1;
assert(new_name[0] != '\0');
/* will happen for trailing /, so be careful */
lookup_path(to_directory, &to_parent, NULL, 1);
fprintf(stderr, "Moving %d/%s to %d/%s\n",
from_parent, name, to_parent, new_name);
if (from_parent != to_parent) {
/* NB it may be quicker to try compare the two strings */
delete_child(from_parent, id);
add_child(to_parent, id);
}
data = get_datum(id); /* get again - in case overwritten */
data = modify_parent(data, to_parent);
if (strcmp(name, new_name) != 0) /* name change too */
data = modify_name(data, new_name);
store_datum(id, data, DBM_REPLACE);
flush_database();
return;
}
void
move_entry_id(from_parent, name, to_parent, new_name)
sdword from_parent, to_parent;
char *name, *new_name;
{
datum data;
sdword id;
char *ptr;
if (lookup_path_id(from_parent, name, &id, NULL, 0) <= 0) {
/* did not know previously! */
add_entry_id(from_parent, name);
return;
}
if (lookup_path_id(to_parent, new_name, NULL, NULL, 0) > 0)
/* exists already! */
return;
if (is_directory(equiv_path(id))||is_file(equiv_path(id)))
return;
if (from_parent == to_parent) {
data = get_datum(id); /* get here in case corrupted earlier */
data = modify_name(data, new_name);
store_datum(id, data, DBM_REPLACE);
} else {
delete_child(from_parent, id);
add_child(to_parent, id);
data = get_datum(id); /* get here in case corrupted earlier */
data = modify_parent(data, to_parent);
if (strcmp(name, new_name) != 0) /* name change too */
data = modify_name(data, new_name);
store_datum(id, data, DBM_REPLACE);
}
if (! is_directory(equiv_path(id)) && ! is_file(equiv_path(id)))
delete_entry_id(id); /* does not actually exist */
else
flush_database();
return;
}
void
rename_entry_id(id, new_name)
sdword id;
char *new_name;
{
datum data;
int ret;
if (verbose)
fprintf(stderr, "rename_entry_id: %d -> %s\n",
id, new_name);
data = get_datum(id);
if (data.dptr == NULL) {
if (verbose)
fprintf(stderr, "Unknown id: %d\n", id);
/* did not know previously! */
return;
}
data = modify_name(data, new_name);
if (ret = store_datum(id, data, DBM_REPLACE) < 0)
if (verbose)
fprintf(stderr, "rename_entry_id: store returned %d\n",
ret);
if (! is_directory(equiv_path(id)) && ! is_file(equiv_path(id))) {
if (verbose)
fprintf(stderr, "rename_entry_id: Ooops! %s doesn't exist!\n",
equiv_path(id));
delete_entry_id(id); /* does not actually exist */
} else
flush_database();
return;
}
void
rename_entry(path, new_name)
char *path, *new_name;
{
datum data;
sdword id;
char *ptr;
if (lookup_path(path, &id, &data, 0) <= 0) {
/* did not know previously! */
return;
}
data = modify_name(data, new_name);
store_datum(id, data, DBM_REPLACE);
if (! is_directory(equiv_path(id)) && ! is_file(equiv_path(id)))
delete_entry_id(id); /* does not actually exist */
else
flush_database();
return;
}
void
clean_entries()
{
/* currently no-op */
continue_clean = 0;
return;
}
void
session()
{
struct sockaddr addr;
int addrlen;
int sock;
char buff[2*MAXPATHLEN+3];
char command, arg1[MAXPATHLEN], arg2[MAXPATHLEN];
sdword id1, id2;
int count;
int prob, args, fileID;
for (;;) {
fileID = 0;
addrlen = sizeof(struct sockaddr);
if ((sock = accept(queries, &addr, &addrlen)) < 0)
fatal("Accept");
if ((count = recv(sock, buff, 2*MAXPATHLEN+3, 0)) < 0)
fatal("Recv");
buff[count] = '\0';
if (verbose && count > 0)
fprintf(stderr, "Received: '%s'\n", buff);
prob = 0;
if (buff[0] != '\0') {
command = buff[0];
switch (command) { /* first decode arguments */
case 'A': case 'D':
args = sscanf(buff+1, "%[^\277]\277", arg1);
break;
case 'M':
args = sscanf(buff+1, "%[^\277]\277%[^\277]\277", arg1, arg2);
break;
case 'R':
args = sscanf(buff+1, "%[^\277]\277%[^\277]\277", arg1, arg2);
break;
case 'a':
args = sscanf(buff+1, "%d\277%[^\277]\277", &id1, arg1);
break;
case 'f':
args = sscanf(buff+1, "%d\277%[^\277]\277", &id1, arg1);
break;
case 'd':
args = sscanf(buff+1, "%d\277", &id1);
break;
case 'm':
args = sscanf(buff+1, "%d\277%[^\277]\277%d\277%[^\277]\277",
&id1, arg1, &id2, arg2);
break;
case 'r':
args = sscanf(buff+1, "%d\277%[^\277]\277", &id1, arg1);
break;
}
switch (command) {
case 'A':
if (args < 1 || arg1[0] != '/')
prob = 1;
else
add_entry(arg1);
break;
case 'a':
if (args < 2)
prob = 1;
else
add_entry_id(id1, arg1);
break;
case 'f':
if (args < 2)
prob = 1;
else
add_entry_fid(id1, arg1);
break;
case 'D':
if (args < 1 || arg1[0] != '/')
prob = 1;
else
delete_entry(arg1);
break;
case 'd':
if (args < 1)
prob = 1;
else
delete_entry_id(id1);
break;
case 'M':
if (args < 2 || arg1[0] != '/' || arg2[0] != '/')
prob = 1;
else
move_entry(arg1, arg2);
break;
case 'm':
if (args < 4)
prob = 1;
else
move_entry_id(id1, arg1, id2, arg2);
break;
case 'R':
if (args < 2 || arg1[0] != '/')
prob = 1;
else
rename_entry(arg1, arg2);
break;
case 'r':
if (args < 2)
prob = 1;
else
rename_entry_id(id1, arg1);
break;
case 'C':
clean_entries();
break;
default:
prob = 1;
break;
}
}
if (prob)
fprintf(stderr, "Bad command '%s'\n", buff);
close(sock);
}
return;
}
/*
* disassociate
*
*/
void
do_disconnect()
{
if (fork())
_exit(0);
{
int f;
for (f = 0; f < 10; f++)
(void) close(f);
}
(void) open("/", 0);
#ifndef NODUP2
(void) dup2(0, 1);
(void) dup2(0, 2);
#else NODUP2
(void)dup(0); /* for slot 1 */
(void)dup(0); /* for slot 2 */
#endif NODUP2
#ifndef POSIX
#ifdef TIOCNOTTY
{
int t;
if ((t = open("/dev/tty", 2)) >= 0) {
ioctl(t, TIOCNOTTY, (char *)0);
(void) close(t);
}
}
#endif TIOCNOTTY
#ifdef xenix5
/*
* USG process groups:
* The fork guarantees that the child is not a process group leader.
* Then setpgrp() can work, whick loses the controllong tty.
* Note that we must be careful not to be the first to open any tty,
* or it will become our controlling tty. C'est la vie.
*
*/
setpgrp();
#endif xenix5
#else POSIX
(void) setsid();
#endif POSIX
}
/*
* These are here to ensure we pick up these
* versions for the server, and not those in lib_client.c
*
*/
int
amAufsExt()
{
return(1);
}
/*
* flush_database: force any outstanding writes
*
*/
void
flush_database()
{
#ifndef USE_GDBM
extern void open_database( /* void */ );
dbm_close(db);
open_database();
#endif /* USE_GDBM */
return;
}
#else FIXED_DIRIDS
#include <stdio.h>
main()
{
printf("afpidsrvr: not compiled with -DFIXED_DIRIDS\n");
}
#endif FIXED_DIRIDS