/* $Header: /afs/athena.mit.edu/astaff/project/layerdev/src/xlayer/RCS/xlayer.c,v 1.10 94/07/18 11:36:08 probe Exp Locker: brlewis $ */

#ifdef POSIX
#include <unistd.h>
#endif
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/mount.h>
#ifdef SOLARIS
#include <sys/statvfs.h>
#endif
#ifdef hpux
#include <sys/vfs.h>
#endif
#ifdef _AIX
#include <sys/statfs.h>
#endif
#ifdef POSIX
#include <dirent.h>
#else
#include <sys/dir.h>
#endif
#include <pwd.h>
#include <X11/IntrinsicP.h>
#include <X11/StringDefs.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xaw/Text.h>
#include "layer.h"


Widget WcFullNameToWidget();


/* Function declarations */
extern void AriRegisterAthena(), localErrorHandler();
void positionDescACT(), toggleOptionCB(), subsetStatusCB(), setPartitionCB();
void doitCB(), saveCB(), loadCB(), clearCB(), popupCB(), rebootCB();
void setPartitionACT(), EnableUpdates(), DisableUpdates();
char *get_config(), *strsave();


/* Routines to find free space on a filesystem */
#ifdef ultrix
#define fsname fd_req.path
#define fsbavail fd_req.bfreen
#define fsbsize fd_req.bsize
#endif
#ifdef SOLARIS
#define fs_data statvfs
#define fsname f_fstr
#define fsbavail f_bavail
#define fsbsize f_bsize
#define statfs statvfs
#define _S_IFMT S_IFMT
#endif
#ifdef hpux
#define fs_data statfs
#define fsname f_spare
#define fsbavail f_bavail
#define fsbsize f_bsize
#endif
#ifdef _AIX
#define fs_data statfs
#define fsname f_fname
#define fsbavail f_bavail
#define fsbsize f_bsize
#endif

/* Private actions */

XtActionsRec actions[] = {
    { "positionDesc", positionDescACT },
    { "Load", loadCB },
    { "Save", saveCB },
    { "SetPartitionACT", setPartitionACT },
};


/* Items on the options menu */

struct layer_option {
    char *name;
    char *widget_name;
    int set;
    int def;
} layer_options[] = {
    { "autoupdate", "*option1", 1, 1},
    { "monitor", "*option2", 1, 1},
    { "register", "*option4", 1, 1},
    { "save", "*option3", 0, 0},
    { "custom", "*option5", 0, 0},
    { "notify", "*option6", 0, 0},
};


/* Globals */

Widget appShell;
Display *dpy;
char *whoami;
char *conf_dir;
char *srvd_dir;
char *layer_athena;
int disk_space;
int debug = 0;
int dont_update = 0;


/* MAIN function */

void
main(argc, argv)
int argc;
char* argv[];
{   
    XtAppContext app;
    XrmDatabase db;
    int i;
    char buf[256], buf2[256];

    conf_dir = SRC_DIR;
    if (access("/srvd/.rvdinfo", R_OK))
      srvd_dir = SRVD_DIR;
    else
      srvd_dir = "/srvd";
    sprintf(buf, "%s/bin/layer_athena", conf_dir);
    layer_athena = strsave(buf);
    quotenewlines = 1;

    for (i = 1; i < argc; i++)
      if (!strcmp(argv[i], "-debug"))
	debug = 1;
      else if (!strcmp(argv[i], "-real"))
	debug = 0;

    if (!debug) {
	FILE *in;

	if (geteuid()) {
	    fprintf(stderr, "Must be root to use %s\n", argv[0]);
	    exit(1);
	}
	if ((in = fopen("/etc/athena/version", "r"))) {
	    while (fgets(buf, sizeof(buf), in));
	    if (!strncmp(buf, "Athena", 6)) {
		fprintf(stderr, "You may only install Layered Athena on a workstation not already\ninstalled with the full Athena release.\n");
		exit(1);
	    }
	    fclose(in);
	}
    }

    whoami = argv[0];
    sprintf(buf, "%s/Layer", conf_dir);
    setenv("XENVIRONMENT", strsave(buf), 1);

    /*
     *  Intialize Toolkit creating the application shell, and get
     *  application resources.
     */
    appShell = XtInitialize ("xlayer", "Layer", NULL, 0,
			     &argc, argv);
    app = XtWidgetToApplicationContext(appShell);
    XtAppAddActions(app, actions, XtNumber(actions));
    WcRegisterCallback(app, "ToggleOption", toggleOptionCB, NULL);
    WcRegisterCallback(app, "SubsetStatus", subsetStatusCB, NULL);
    WcRegisterCallback(app, "DoIt", doitCB, NULL);
    WcRegisterCallback(app, "Save", saveCB, NULL);
    WcRegisterCallback(app, "Load", loadCB, NULL);
    WcRegisterCallback(app, "Clear", clearCB, NULL);
    WcRegisterCallback(app, "Popup", popupCB, NULL);
    WcRegisterCallback(app, "RebootCB", rebootCB, NULL);
    WcRegisterCallback(app, "SetPartition", setPartitionCB, NULL);
    WcRegisterCallback(app, "EnableUpdates", EnableUpdates, NULL);
    WcRegisterCallback(app, "DisableUpdates", DisableUpdates, NULL);
    XtAppSetErrorHandler(app, localErrorHandler);
    dpy = XtDisplay(appShell);

    /* Register all Athena widget classes */
    AriRegisterAthena(app );

    sprintf(buf, "%s/%s", conf_dir, CONF_FILE);
    if (read_syspack(buf)) {
	fprintf(stderr, "Unable to read system configuration.\n");
    }

    strcpy(buf2, "*subsetbox.wcChildren: space,");
    db = XtDatabase(dpy);
    for (i = 0; sp_subsets[i]; i++) {
	strcat(buf2, sp_subsets[i]->box);
	strcat(buf2, ",");
	sprintf(buf, "*%s.wcClassName: Form", sp_subsets[i]->box);
	XrmPutLineResource(&db, buf);
	sprintf(buf, "*%s.wcChildren: ssname, status, statMenu, size, descPU",
		sp_subsets[i]->box);
	XrmPutLineResource(&db, buf);
	if (i > 0) {
	    sprintf(buf, "*%s.fromVert: %s", sp_subsets[i]->box,
		    sp_subsets[i-1]->box);
	    XrmPutLineResource(&db, buf);
	}
	sprintf(buf, "*%s.ssname.label: %s", sp_subsets[i]->box,
		sp_subsets[i]->name);
	XrmPutLineResource(&db, buf);
	sprintf(buf, "*%s*descText*string: %s", sp_subsets[i]->box,
		sp_subsets[i]->desc);
	sprintf(buf, "*%s*descLbl.label: %s", sp_subsets[i]->box,
		sp_subsets[i]->desc);
	XrmPutLineResource(&db, buf);
	if (sp_subsets[i]->flags & F_LOCALONLY) {
	    sprintf(buf, "*%s*statM2.sensitive: False\n", sp_subsets[i]->box);
	    XrmPutLineResource(&db, buf);
	}
	if (sp_subsets[i]->flags & F_DISABLED) {
	    sprintf(buf, "*%s.status.sensitive: False\n", sp_subsets[i]->box);
	    XrmPutLineResource(&db, buf);
	}
    }
    strcat(buf2, "totalbox");
    XrmPutLineResource(&db, buf2);

    /*  Create widget tree below toplevel shell using Xrm database */
    WcWidgetCreation(appShell);

    /*  Realize the widget tree, finish up initializing,
     *  and enter the main application loop */
    XtRealizeWidget(appShell);
    DisableUpdates();
    InitFields(appShell, "");
    EnableUpdates();

    XtMainLoop();
}


/* Initialize all of the fields in the client based on the current
 * configuration of the workstation */

char *hostname = NULL;

InitFields(parent, file)
Widget parent;
char *file;
{
    Widget w;
    struct passwd *pwd;
    struct stat stb;
    struct fs_data stf;
    uid_t uid;
    Arg args[2];
    int i;
    static char buf[256];
    char *p;

    clear_config();
    set_config(file);

    if (hostname == NULL) {
	gethostname(buf, sizeof(buf));
	hostname = strsave(buf);

	sprintf(buf, "%s/%s", conf_dir, CONF_FILE);

	if (read_syspack(buf)) {
	    fprintf(stderr, "Unable to read system configuration.\n");
	}
    }

    /* Set the workstation name */
    sprintf(buf, "Workstation: %s", hostname);
    XtSetArg(args[0], XtNlabel, strsave(buf));
    w = WcFullNameToWidget(parent, "*host");
    if (w) XtSetValues(w, args, 1);

    /* Set the owner & location */
    if (p = get_config("owner"))
      XtSetArg(args[0], XtNstring, strsave(p));
    else {
	uid = getuid();
	if (!uid) uid = geteuid();
	if (uid && (pwd = getpwuid(uid)))
	  sprintf(buf, "%s@mit.edu", pwd->pw_name);
	else
	  sprintf(buf, "root@%s", hostname);
	XtSetArg(args[0], XtNstring, strsave(buf));
    }
    w = WcFullNameToWidget(parent, "*ownerf");
    if (w) XtSetValues(w, args, 1);
    if (!(p = get_config("location")))
      p = "";
    XtSetArg(args[0], XtNstring, strsave(p));
    w = WcFullNameToWidget(parent, "*locf");
    if (w) XtSetValues(w, args, 1);

    /* init the menu options */
    for (i = 0; i < sizeof(layer_options)/sizeof(struct layer_option); i++) {
	if (p = get_config(layer_options[i].name)) {
	    w = WcFullNameToWidget(parent, layer_options[i].widget_name);
	    layer_options[i].set = atoi(p);
	    if (layer_options[i].set)
	      WcSetValue(w, "this.leftBitmap: black6");
	    else
	      WcSetValue(w, "this.leftBitmap: box6");
	    if (!strcmp(layer_options[i].name, "save") && *p) {
		WcSetValue(w, "this.leftBitmap: black6");
		XtSetArg(args[0], XtNstring, p);
		w = WcFullNameToWidget(parent, "*getSavePath*value");
		if (w) XtSetValues(w, args, 1);
	    } else if (!strcmp(layer_options[i].name, "custom") && *p) {
		WcSetValue(w, "this.leftBitmap: black6");
		XtSetArg(args[0], XtNstring, p);
		w = WcFullNameToWidget(parent, "*getCustomPath*value");
		if (w) XtSetValues(w, args, 1);
	    }
	}
    }

    /* init the subset config */
    for (i = 0; sp_subsets[i]; i++) {
	sprintf(buf, "subset %s", sp_subsets[i]->name);
	p = get_config(buf);
	if (!p)
	  p = "ignore";
	if (!strcmp(p, "ignore"))
	  sp_subsets[i]->state = ST_IGNORE;
	else if (!strcmp(p, "remote"))
	  sp_subsets[i]->state = ST_REMOTE;
	else if (!strcmp(p, "local"))
	  sp_subsets[i]->state = ST_LOCAL;
	sprintf(buf, "%s,%s", p, sp_subsets[i]->box);
	subsetStatusCB(parent, buf, NULL);
    }

    /* Now do the free disk space and partition labels */
    sprintf(buf, "%s/.", CONF_DIR);
    strcpy(stf.fsname, "?");
    if (statfs(buf, &stf) != 1) {
	if (debug) {
	    statfs("/usr", &stf);
	} else {
	    mkdir("/usr/layered_athena", 0777);
	    symlink("/usr/layered_athena", CONF_DIR);
	    statfs(buf, &stf);
	}
    }
    XtSetArg(args[0], XtNlabel, stf.fsname);
    w = WcFullNameToWidget(parent, "*part");
    if (w) XtSetValues(w, args, 1);
    XtSetArg(args[0], XtNvalue, stf.fsname);
    w = WcFullNameToWidget(parent, "*partition");
    if (w) XtSetValues(w, args, 1);
    disk_space = stf.fsbavail * stf.fsbsize / 1024 + disk_usage(buf);
    if (!strcmp(stf.fsname, "?"))
      strcpy(buf, "?");
    else
      sprintf(buf, "%d", disk_space);
    XtSetArg(args[0], XtNlabel, buf);
    w = WcFullNameToWidget(parent, "*free");
    if (w) XtSetValues(w, args, 1);
}


int disk_usage(dir)
char *dir;
{
    struct stat stb;
    int total;
    DIR *d;
#ifdef vax
    struct direct *de;
#else
    struct dirent *de;
#endif
    char buf[256];

    if (lstat(dir, &stb))
      return(0);
    total = (stb.st_size + 1023) / 1024;

    if (!S_ISDIR(stb.st_mode))
      return (total);

    if (!(d = opendir(dir)))
      return(total);
    while (de = readdir(d))
      if (strcmp(de->d_name, ".") && strcmp(de->d_name, "..")) {
	  sprintf(buf, "%s/%s", dir, de->d_name);
	  total += disk_usage(buf);
      }

    closedir(d);
    return(total);
}


void setPartitionACT(w, event, params, num_params)
Widget w;
XEvent *event;
String *params;
Cardinal *num_params;
{
    setPartitionCB(w, params[0], NULL);
}


void setPartitionCB(parent, arg, unused)
Widget parent;
char *arg;
caddr_t unused;
{
    char buf[256], *newpart;
    struct fs_data stf;
    struct stat stb, stb2;
    Widget w;
    Arg args[2];

    if (!strcmp(arg, "OK")) {
	w = WcFullNameToWidget(parent, "*partition.value");
	XtSetArg(args[0], XtNstring, &newpart);
	XtGetValues(w, args, 1);
    } else if (!strcmp(arg, "default")) {
	newpart = "/usr";
    } else
      return;

    /* Now do the free disk space and partition labels */
    sprintf(buf, "%s/.", CONF_DIR);
    if (statfs(buf, &stf) < 0) {
	fprintf(stderr, "Unable to find directory %s\n", buf);
	return;
    }

    /* printf("Currently on %s, requested %s\n", stf.fsname, newpart); */
    if (!strcmp(stf.fsname, newpart))
      return;
    if (debug) {
	printf("In debug mode, not changing partition by linking /var/athena to %s/layered_athena\n", newpart);
	return;
    }

    lstat(CONF_DIR, &stb);
    sprintf(buf, "%s/layered_athena", newpart);
    if (lstat(buf, &stb2)) {
	lstat(newpart, &stb2);
    }
    if ((stb.st_mode & _S_IFMT) == S_IFLNK) {
	unlink(CONF_DIR);
	if (stb.st_dev == stb2.st_dev) {
	    mkdir(CONF_DIR, 0777);
	} else {
	    mkdir(buf, 0777);
	    symlink(buf, CONF_DIR);
	}
    } else if (stb.st_dev != stb2.st_dev) {
	sprintf(buf, "%s.old", CONF_DIR);
	link(CONF_DIR, buf);
	unlink(CONF_DIR);
	sprintf(buf, "%s/layered_athena", newpart);
	mkdir(buf, 0777);
	symlink(buf, CONF_DIR);
    }
    statfs(buf, &stf);

    XtSetArg(args[0], XtNlabel, stf.fsname);
    w = WcFullNameToWidget(parent, "*part");
    if (w) XtSetValues(w, args, 1);
    disk_space = stf.fsbavail * stf.fsbsize / 1024 + disk_usage(buf);
    sprintf(buf, "%d", disk_space);
    XtSetArg(args[0], XtNlabel, buf);
    w = WcFullNameToWidget(parent, "*free");
    if (w) XtSetValues(w, args, 1);
}


/* Position a popup directly to the right of its parent */

void positionDescACT(w, event, params, num_params)
Widget w;
XEvent *event;
String *params;
Cardinal *num_params;
{
    Position x, y, newx, newy;
    Widget shell, WcFullNameToWidget();
    Arg arglist[2];

    if (*num_params != 1) {
	char error_buf[BUFSIZ];
	sprintf(error_buf, "%s %s",
		"layer: positionDescACT: position action expects only one",
		"parameter which is the name of the menu.");
	XtAppWarning(XtWidgetToApplicationContext(w), error_buf);
	return;
    }

    if ((shell = WcFullNameToWidget(w, params[0])) == NULL) {
	char error_buf[BUFSIZ];
	sprintf(error_buf, "%s '%s'",
		"layer: could not find widget named: ", params[0]);
	XtAppWarning(XtWidgetToApplicationContext(w), error_buf);
	return;
    }

    x = w->core.x + w->core.width + 3 * w->core.border_width;
    y = w->core.y;
    XtTranslateCoords(w->core.parent, x, y, &newx, &newy);
    x = newx;
    y = newy;

    XtRealizeWidget(shell);
    if (x + shell->core.width + 2 * shell->core.border_width >
	WidthOfScreen(XtScreen(shell)))
      x = WidthOfScreen(XtScreen(shell)) -
	shell->core.width + 2 * shell->core.border_width;
    if (y + shell->core.height + 2 * shell->core.border_width >
	HeightOfScreen(XtScreen(shell)))
      y = HeightOfScreen(XtScreen(shell)) -
	shell->core.height + 2 * shell->core.border_width;
    if (x < 0) x = 0;
    if (y < 0) y = 0;

    XtSetArg(arglist[0], XtNx, x);
    XtSetArg(arglist[1], XtNy, y);
    XtSetValues(shell, arglist, 2);
    XUngrabPointer(dpy, CurrentTime);
}


PopupUnderMouse(w, center, grab)
Widget w;
int center;
int grab;
{
    int dummy;
    int x, y;
    Window wdummy;

    XtRealizeWidget(w);
    if (center) {
	x = WidthOfScreen(XtScreen(w))/2 - w->core.width/2;
	y = HeightOfScreen(XtScreen(w))/2 - w->core.height/2;
    } else {
	XQueryPointer(XtDisplay(w), XtWindow(w), &wdummy, &wdummy, &x, &y,
		      &dummy, &dummy, &dummy);
	x -= 16;
	y -= 16;
    }
    XtMoveWidget(w, x, y);
    XtPopup(w, grab);
}


void popupCB(parent, wname, unused)
Widget parent;
char *wname;
caddr_t unused;
{
    PopupUnderMouse(WcFullNameToWidget(parent, wname), 0, XtGrabExclusive);
}


/* Called from within the toolkit */
void localErrorHandler(s)
String s;
{
    fprintf(stderr, "Layer X error: %s\n", s);
    exit(1);
}


void toggleOptionCB(parent, option, unused)
Widget parent;
char *option;
caddr_t unused;
{
    Arg args[2];
    Widget w;
    char *value;
    int i;

    for (i = 0; i < sizeof(layer_options)/sizeof(struct layer_option); i++) {
	if (!strcmp(option, layer_options[i].name)) {
	    layer_options[i].set = !layer_options[i].set;
	    w = WcFullNameToWidget(parent, layer_options[i].widget_name);
	    if (layer_options[i].set)
	      WcSetValue(w, "this.leftBitmap: black6");
	    else
	      WcSetValue(w, "this.leftBitmap: box6");
	    if (layer_options[i].set) {
		if (!strcmp(option, "save"))
		  PopupUnderMouse(WcFullNameToWidget(parent, "*getSavePath"),
				  0, XtGrabExclusive);
		else if (!strcmp(option, "custom"))
		  PopupUnderMouse(WcFullNameToWidget(parent, "*getCustomPath"),
				  0, XtGrabExclusive);
	    }
	}
    }
}


void subsetStatusCB(parent, stat, unused)
Widget parent;
char *stat;
caddr_t unused;
{
    Widget w;
    Arg args[2];
    int sset;
    char buf[256], status[256], sizename[256], statname[256];
    char *box; 

    strcpy(status, stat);
    if (box = strchr(status, ','))
      *box++ = 0;
    else
      box = parent->core.parent->core.parent->core.name;
    sprintf(sizename, "*%s.size", box);
    sprintf(statname, "*%s.status", box);
    for (sset = 0; sp_subsets[sset]; sset++)
      if (!strcmp(sp_subsets[sset]->box, box)) break;
    if (sp_subsets[sset]->flags & F_DISABLED)
      return;

    DisableUpdates();

    /* set status field */
    XtSetArg(args[0], XtNlabel, status);
    w = WcFullNameToWidget(parent, statname);
    if (w) XtSetValues(w, args, 1);
    sp_subsets[sset]->prevstate = sp_subsets[sset]->state;
    sp_subsets[sset]->flags |= F_CHANGED;

    /* update size */
    if (!strcmp(status, "ignore")) {
	strcpy(buf, "0");
	sp_subsets[sset]->state = ST_IGNORE;
    } else if (!strcmp(status, "local")) {
	sprintf(buf, "%d", sp_subsets[sset]->localsize);
	sp_subsets[sset]->state = ST_LOCAL;
    } else if (!strcmp(status, "remote")) {
	sprintf(buf, "%d", sp_subsets[sset]->remotesize);
	sp_subsets[sset]->state = ST_REMOTE;
    }
    XtSetArg(args[0], XtNlabel, buf);
    w = WcFullNameToWidget(parent, sizename);
    if (w) XtSetValues(w, args, 1);
    EnableUpdates();
}


void saveCB(w, s, unused)
Widget w;
char *s;
caddr_t unused;
{
    char buf[256], cmd[1024];
    char *value, *p;
    Arg args[3];
    int i, on;

    XtSetArg(args[0], XtNstring, &value);
    XtGetValues(WcFullNameToWidget(w, "*savpath.value"), args, 1);
    if (!value || !*value || !strcmp(value, "_")) {
	XtSetArg(args[0], XtNstring, "");
	XtSetValues(WcFullNameToWidget(w, "*savpath.value"), args, 1);
	buf[0] = 0;
    } else {
	sprintf(buf, " config=\"%s\"", value);
    }

    sprintf(cmd, "%s set syspack=\"%s\"", layer_athena, srvd_dir);
    strcat(cmd, buf);

    XtSetArg(args[0], XtNstring, &value);
    XtGetValues(WcFullNameToWidget(w, "*ownerf"), args, 1);
    p = get_config("owner");
    if (!p || strcmp(value, p)) {
	sprintf(buf, " owner=\"%s\"", value);
	strcat(cmd, buf);
    }
    XtSetArg(args[0], XtNstring, &value);
    XtGetValues(WcFullNameToWidget(w, "*locf"), args, 1);
    p = get_config("location");
    if (!p || strcmp(value, p)) {
	sprintf(buf, " location=\"%s\"", value);
	strcat(cmd, buf);
    }

    for (i = 0; i < sizeof(layer_options)/sizeof(struct layer_option); i++) {
	on = layer_options[i].set;
	p = get_config(layer_options[i].name);
	if (!strcmp(layer_options[i].name, "save")) {
	    XtSetArg(args[0], XtNstring, &value);
	    XtGetValues(WcFullNameToWidget(w, "*getsave*value"), args, 1);
	    if ((!on || on && !*value) && p && *p)
	      strcat(cmd, " nosave");
	    else if (on && strcmp(p, value)) {
		sprintf(buf, " save=\"%s\"", value);
		strcat(cmd, buf);
	    }
	} else if (!strcmp(layer_options[i].name, "custom")) {
	    XtSetArg(args[0], XtNstring, &value);
	    XtGetValues(WcFullNameToWidget(w, "*getcustom*value"), args, 1);
	    if ((!on || on && !*value) && p && *p)
	      strcat(cmd, " nocustom");
	    else if (on && strcmp(p, value)) {
		sprintf(buf, " custom=\"%s\"", value);
		strcat(cmd, buf);
	    }
	} else if (!strcmp(layer_options[i].name, "notify")) {
	    if (on) {
		sprintf(buf, " notify=%s", sp_version);
		strcat(cmd, buf);
	    }
	} else if (!p || ((!on && atoi(p)) || (on && !atoi(p)))) {
	    sprintf(buf, " %s%s", on ? "" : "no", layer_options[i].name);
	    strcat(cmd, buf);
	}
    }

    for (i = 0; sp_subsets[i]; i++) {
	XtSetArg(args[0], XtNlabel, &value);
	sprintf(buf, "*%s.status", sp_subsets[i]->box);
	XtGetValues(WcFullNameToWidget(w, buf), args, 1);
	sprintf(buf, "subset %s", sp_subsets[i]->name);
	p = get_config(buf);
	if ((!p || strcmp(value, p)) && (strcmp(value, "ignore") || p && *p)) {
	    sprintf(buf, " %s=%s", sp_subsets[i]->name, value);
	    strcat(cmd, buf);
	}
    }

    if (debug)
      fprintf(stderr, "Executing: %s\n", cmd);
    else
      system(cmd);
    clear_config();
}


void doitCB(parent, s, unused)
Widget parent;
char *s;
caddr_t unused;
{
    char cmd[1024];
    Widget w;
    Arg args[2];
    int status = 0;

    saveCB(parent, s, NULL);
    sprintf(cmd, "%s update syspack=\"%s\"", layer_athena, srvd_dir);
    if (debug)
      fprintf(stderr, "Executing: %s\n", cmd);
    else
      status = system(cmd);
    if (status) {
	sprintf(cmd, "Update failed, status %d\n", status);
	w = WcFullNameToWidget(parent, "*dependShell");
	XtRealizeWidget(w);
	XtSetArg(args[0], XtNstring, strsave(cmd));
	XtSetValues(WcFullNameToWidget(parent, "*dtext"), args, 1);
	w = WcFullNameToWidget(parent, "*dependShell");
    } else {
	w = WcFullNameToWidget(parent, "*doneShell");
    }
    if (w) PopupUnderMouse(w, 1, XtGrabExclusive);
}


void DisableUpdates()
{
    dont_update++;
}


void EnableUpdates()
{
    Widget w, parent;
    Arg args[2];
    int i, j, failure, sset, total, prev;
    struct spsubset *ss;
    char buf[256], msg[1024];
    char *value, *status; 

    if (--dont_update > 0)
      return;
    dont_update = 0;

    /* checking is now allowed; sanity check everything */
    failure = 0;
    msg[0] = 0;
    parent = appShell;

    for (sset = 0; ss = sp_subsets[sset]; sset++) {
	if (!(sp_subsets[sset]->flags & F_CHANGED))
	  continue;
	XtSetArg(args[0], XtNlabel, &status);
	sprintf(buf, "*%s.status", ss->box);
	w = WcFullNameToWidget(parent, buf);
	if (w) XtGetValues(w, args, 1);
	if (strcmp(status, "ignore") & ss->flags & F_DISABLED) {
	    sprintf(buf, "Subset \"%s\" is currently disabled.\n", ss->name);
	    strcat(msg, buf);
	    failure++;
	}
	if (!strcmp(status, "remote") && ss->flags & F_LOCALONLY) {
	    sprintf(buf, "Subset \"%s\" may not be used remotely.\n", ss->name);
	    strcat(msg, buf);
	    failure++;
	}

	if (!strcmp(status, "ignore")) {
	    /* We may have turned off a subset */
	    for (i = 0; sp_subsets[i]; i++)
	      for (j = 0; sp_subsets[i]->depends[j]; j++)
		if (sp_subsets[i]->depends[j] == ss) {
		    XtSetArg(args[0], XtNlabel, &value);
		    sprintf(buf, "*%s.status", sp_subsets[i]->box);
		    w = WcFullNameToWidget(parent, buf);
		    if (w) XtGetValues(w, args, 1);
		    if (strcmp(value, "ignore")) {
			sprintf(buf, "Subset \"%s\" must be disabled before subset \"%s\"\n  can be turned off.\n",
				sp_subsets[i]->name, ss->name);
			strcat(msg, buf);
			prev = ss->prevstate;
			sprintf(buf, "%s,%s",
				ss->prevstate == ST_LOCAL ? "local" : "remote",
				ss->box);
			subsetStatusCB(w, buf, 0);
			ss->prevstate = prev;
			failure++;
		    }
		}
	} else if (strcmp(status, "ignore")) {
	    /* We may have turned on a subset */
	    for (i = 0; ss->depends[i] && i < MAX_SUBSETS; i++) {
		XtSetArg(args[0], XtNlabel, &value);
		sprintf(buf, "*%s.status", ss->depends[i]->box);
		w = WcFullNameToWidget(parent, buf);
		if (w) XtGetValues(w, args, 1);
		if (!strcmp(value, "ignore")) {
		    sprintf(buf, "Subset \"%s\" must be enabled before subset \"%s\"\n  can be turned on.\n",
			    ss->depends[i]->name, ss->name);
		    strcat(msg, buf);
		    sprintf(buf, "ignore,%s", ss->box);
		    subsetStatusCB(w, buf, 0);
		    failure++;
		}
	    }
	}
	sp_subsets[sset]->flags &= ~F_CHANGED;
    }

    /* compute & update total size */
    total = 0;
    for (i = 0; sp_subsets[i]; i++) {
	buf[0] = '*';
	strcpy(&buf[1], sp_subsets[i]->name);
	buf[4] = 0;
	strcat(buf, "box*size");
	XtSetArg(args[0], XtNlabel, &value);
	w = WcFullNameToWidget(parent, buf);
	if (w) XtGetValues(w, args, 1);
	total += atoi(value);
    }
    sprintf(buf, "%d", total);
    XtSetArg(args[0], XtNlabel, buf);
    w = WcFullNameToWidget(w, "*totalbox*size");
    if (w) XtSetValues(w, args, 1);
    if (total > disk_space) {
	strcat(msg,
	       "Projected disk usage will not fit on specified partition!\n");
	failure++;
    }

    if (failure) {
	w = WcFullNameToWidget(parent, "*dependShell");
	XtRealizeWidget(w);
	XtSetArg(args[0], XtNstring, strsave(msg));
	XtSetValues(WcFullNameToWidget(parent, "*dtext"), args, 1);
	PopupUnderMouse(w, 1, XtGrabNone);
	return;
    }
}


void loadCB(w, s, unused)
Widget w;
char *s;
caddr_t unused;
{
    char *value;
    Arg args[2];

    XtSetArg(args[0], XtNstring, &value);
    XtGetValues(WcFullNameToWidget(w, "*loadpath.value"), args,1);
    DisableUpdates();
    clear_config();
    InitFields(w, value);
    EnableUpdates();
}


void clearCB(parent, s, unused)
Widget parent;
char *s;
caddr_t unused;
{
    Widget w;
    struct passwd *pwd;
    uid_t uid;
    Arg args[2];
    int i;
    char *p, buf[256];

    /* Set the owner & location */
    uid = getuid();
    if (!uid) uid = geteuid();
    if (uid && (pwd = getpwuid(uid)))
      sprintf(buf, "%s@mit.edu", pwd->pw_name);
    else
      sprintf(buf, "root@%s", hostname);
    XtSetArg(args[0], XtNstring, strsave(buf));
    w = WcFullNameToWidget(parent, "*ownerf");
    if (w) XtSetValues(w, args, 1);
    XtSetArg(args[0], XtNstring, "");
    w = WcFullNameToWidget(parent, "*locf");
    if (w) XtSetValues(w, args, 1);

    /* init the menu options */
    for (i = 0; i < sizeof(layer_options)/sizeof(struct layer_option); i++) {
	w = WcFullNameToWidget(parent, layer_options[i].widget_name);
	layer_options[i].set = layer_options[i].def;
	if (layer_options[i].set)
	  WcSetValue(w, "this.leftBitmap: black6");
	else
	  WcSetValue(w, "this.leftBitmap: box6");
    }
    XtSetArg(args[0], XtNstring, "");
    w = WcFullNameToWidget(parent, "*getSavePath*value");
    if (w) XtSetValues(w, args, 1);
    w = WcFullNameToWidget(parent, "*getCustomPath*value");
    if (w) XtSetValues(w, args, 1);

    w = WcFullNameToWidget(parent, "*standard");
    if (w) XtCallCallbacks(w, "callback", "");
}


void rebootCB()
{
    if (debug) {
	printf("Reboot requested!\n");
	return;
    }

    system("sync; sync; sync; reboot");
}
