]> andersk Git - moira.git/commitdiff
Initial revision
authormar <mar>
Mon, 1 Nov 1993 12:04:03 +0000 (12:04 +0000)
committermar <mar>
Mon, 1 Nov 1993 12:04:03 +0000 (12:04 +0000)
server/qaccess.dc [new file with mode: 0644]
server/qfollow.dc [new file with mode: 0644]
server/qsetup.dc [new file with mode: 0644]
server/qvalidate.dc [new file with mode: 0644]

diff --git a/server/qaccess.dc b/server/qaccess.dc
new file mode 100644 (file)
index 0000000..234e560
--- /dev/null
@@ -0,0 +1,338 @@
+/*
+ *     $Source$
+ *     $Author$
+ *     $Header$
+ *
+ *     Copyright (C) 1987 by the Massachusetts Institute of Technology
+ *     For copying and distribution information, please see the file
+ *     <mit-copyright.h>.
+ *
+ */
+
+#ifndef lint
+static char *rcsid_qsupport_dc = "$Header$";
+#endif lint
+
+#include <mit-copyright.h>
+#include "query.h"
+#include "mr_server.h"
+#include <ctype.h>
+EXEC SQL INCLUDE sqlca;
+EXEC SQL INCLUDE sqlda;
+#include "qrtn.h"
+
+extern char *whoami;
+extern int ingres_errno, mr_errcode;
+
+EXEC SQL BEGIN DECLARE SECTION;
+extern char stmt_buf[];
+EXEC SQL END DECLARE SECTION;
+
+EXEC SQL WHENEVER SQLERROR CALL ingerr;
+
+
+/* Specialized Access Routines */
+
+/* access_user - verify that client name equals specified login name
+ *
+ *  - since field validation routines are called first, a users_id is
+ *    now in argv[0] instead of the login name.
+ */
+
+access_user(q, argv, cl)
+    struct query *q;
+    char *argv[];
+    client *cl;
+{
+    if (cl->users_id != *(int *)argv[0])
+       return(MR_PERM);
+    else
+       return(MR_SUCCESS);
+}
+
+
+
+/* access_login - verify that client name equals specified login name
+ *
+ *   argv[0...n] contain search info.  q->
+ */
+
+access_login(q, argv, cl)
+    struct query *q;
+    char *argv[];
+    client *cl;
+{
+    EXEC SQL BEGIN DECLARE SECTION;
+    int id;
+    char qual[256];
+    EXEC SQL END DECLARE SECTION;
+
+    build_qual(q->qual, q->argc, argv, qual);
+    if (!strncmp(q->name,"get_user_account",strlen("get_user_account"))) {
+       EXEC SQL SELECT users_id INTO :id FROM users u, strings str WHERE :qual;
+    } else {
+       EXEC SQL SELECT users_id INTO :id FROM users u WHERE :qual;
+    }
+
+    if (sqlca.sqlerrd[2] != 1 || id != cl->users_id)
+       return(MR_PERM);
+    else
+       return(MR_SUCCESS);
+}
+
+
+
+/* access_list - check access for most list operations
+ *
+ * Inputs: argv[0] - list_id
+ *         q - query name
+ *         argv[2] - member ID (only for queries "amtl" and  "dmfl")
+ *         argv[7] - group IID (only for query "ulis")
+ *          cl - client name
+ *
+ * - check that client is a member of the access control list
+ * - OR, if the query is add_member_to_list or delete_member_from_list
+ *     and the list is public, allow access if client = member
+ */
+
+access_list(q, argv, cl)
+    struct query *q;
+    char *argv[];
+    client *cl;
+{
+    EXEC SQL BEGIN DECLARE SECTION;
+    int list_id, acl_id, flags, gid;
+    char acl_type[9];
+    EXEC SQL END DECLARE SECTION;
+    char *client_type;
+    int client_id, status;
+
+    list_id = *(int *)argv[0];
+    EXEC SQL SELECT acl_id, acl_type, gid, publicflg
+      INTO :acl_id, :acl_type, :gid, :flags
+      FROM list
+      WHERE list_id = :list_id;
+
+    if (sqlca.sqlerrd[2] != 1)
+      return(MR_INTERNAL);
+
+    /* parse client structure */
+    if ((status = get_client(cl, &client_type, &client_id)) != MR_SUCCESS)
+       return(status);
+
+    /* if amtl or dmfl and list is public allow client to add or delete self */
+    if (((!strcmp("amtl", q->shortname) && flags) ||
+        (!strcmp("dmfl", q->shortname))) &&
+       (!strcmp("USER", argv[1]))) {
+       if (*(int *)argv[2] == client_id) return(MR_SUCCESS);
+    /* if update_list, don't allow them to change the GID */
+    } else if (!strcmp("ulis", q->shortname)) {
+       if ((!strcmp(argv[7], UNIQUE_GID) && (gid != -1)) ||
+           (strcmp(argv[7], UNIQUE_GID) && (gid != atoi(argv[7]))))
+         return(MR_PERM);
+    }
+
+    /* check for client in access control list */
+    status = find_member(acl_type, acl_id, client_type, client_id, 0);
+    if (!status) return(MR_PERM);
+
+    return(MR_SUCCESS);
+}
+
+
+/* access_visible_list - allow access to list only if it is not hidden,
+ *     or if the client is on the ACL
+ *
+ * Inputs: argv[0] - list_id
+ *         cl - client identifier
+ */
+
+access_visible_list(q, argv, cl)
+    struct query *q;
+    char *argv[];
+    client *cl;
+{
+    EXEC SQL BEGIN DECLARE SECTION;
+    int list_id, acl_id, flags ;
+    char acl_type[9];
+    EXEC SQL END DECLARE SECTION;
+    char *client_type;
+    int client_id, status;
+
+    list_id = *(int *)argv[0];
+    EXEC SQL SELECT hidden, acl_id, acl_type
+      INTO :flags, :acl_id, :acl_type
+      FROM list
+      WHERE list_id = :list_id;
+    if (sqlca.sqlerrd[2] != 1)
+      return(MR_INTERNAL);
+    if (!flags)
+        return(MR_SUCCESS);
+
+    /* parse client structure */
+    if ((status = get_client(cl, &client_type, &client_id)) != MR_SUCCESS)
+       return(status);
+
+    /* check for client in access control list */
+    status = find_member(acl_type, acl_id, client_type, client_id, 0);
+    if (!status)
+       return(MR_PERM);
+
+    return(MR_SUCCESS);
+}
+
+
+/* access_vis_list_by_name - allow access to list only if it is not hidden,
+ *     or if the client is on the ACL
+ *
+ * Inputs: argv[0] - list name
+ *         cl - client identifier
+ */
+
+access_vis_list_by_name(q, argv, cl)
+    struct query *q;
+    char *argv[];
+    client *cl;
+{
+    EXEC SQL BEGIN DECLARE SECTION;
+    int acl_id, flags, rowcount;
+    char acl_type[9], *listname;
+    EXEC SQL END DECLARE SECTION;
+    char *client_type;
+    int client_id, status;
+
+    listname = argv[0];
+    EXEC SQL SELECT hidden, acl_id, acl_type INTO :flags, :acl_id, :acl_type
+      FROM list WHERE name = :listname;
+
+    rowcount=sqlca.sqlerrd[2];
+    if (rowcount > 1)
+      return(MR_WILDCARD);
+    if (rowcount == 0)
+      return(MR_NO_MATCH);
+    if (!flags)
+       return(MR_SUCCESS);
+
+    /* parse client structure */
+    if ((status = get_client(cl, &client_type, &client_id)) != MR_SUCCESS)
+       return(status);
+
+    /* check for client in access control list */
+    status = find_member(acl_type, acl_id, client_type, client_id, 0);
+    if (!status)
+       return(MR_PERM);
+
+    return(MR_SUCCESS);
+}
+
+
+/* access_member - allow user to access member of type "USER" and name matches
+ * username, or to access member of type "LIST" and list is one that user is
+ * on the acl of, or the list is visible.
+ */
+
+access_member(q, argv, cl)
+    struct query *q;
+    char *argv[];
+    client *cl;
+{
+    if (!strcmp(argv[0], "LIST") || !strcmp(argv[0], "RLIST"))
+      return(access_visible_list(q, &argv[1], cl));
+
+    if (!strcmp(argv[0], "USER") || !strcmp(argv[0], "RUSER")) {
+       if (cl->users_id == *(int *)argv[1])
+         return(MR_SUCCESS);
+    }
+
+    if (!strcmp(argv[0], "KERBEROS") || !strcmp(argv[0], "RKERBERO")) {
+        if (cl->client_id == *(int *)argv[1])
+         return(MR_SUCCESS);
+    }
+
+    return(MR_PERM);
+}
+
+
+/* access_qgli - special access routine for Qualified_get_lists.  Allows
+ * access iff argv[0] == "TRUE" and argv[2] == "FALSE".
+ */
+
+access_qgli(q, argv, cl)
+    struct query *q;
+    char *argv[];
+    client *cl;
+{
+    if (!strcmp(argv[0], "TRUE") && !strcmp(argv[2], "FALSE"))
+      return(MR_SUCCESS);
+    return(MR_PERM);
+}
+
+
+/* access_service - allow access if user is on ACL of service.  Don't
+ * allow access if a wildcard is used.
+ */
+
+access_service(q, argv, cl)
+    struct query *q;
+    char *argv[];
+    client *cl;
+{
+    EXEC SQL BEGIN DECLARE SECTION;
+    int acl_id;
+    char *name, acl_type[9];
+    EXEC SQL END DECLARE SECTION;
+    int client_id, status;
+    char *client_type, *c;
+
+    name = argv[0];
+    for(c=name;*c;c++) if(islower(*c)) *c = toupper(*c);  /* uppercasify */
+    EXEC SQL SELECT acl_id, acl_type INTO :acl_id, :acl_type FROM servers
+      WHERE name = :name;
+    if (sqlca.sqlerrd[2] > 1)
+      return(MR_PERM);
+
+    /* parse client structure */
+    if ((status = get_client(cl, &client_type, &client_id)) != MR_SUCCESS)
+       return(status);
+
+    /* check for client in access control list */
+    status = find_member(acl_type, acl_id, client_type, client_id, 0);
+    if (!status) return(MR_PERM);
+
+    return(MR_SUCCESS);
+}
+
+
+/* access_filesys - verify that client is owner or on owners list of filesystem
+ *     named by argv[0]
+ */
+
+access_filesys(q, argv, cl)
+    struct query *q;
+    char *argv[];
+    client *cl;
+{
+    EXEC SQL BEGIN DECLARE SECTION;
+    int users_id, list_id;
+    char *name;
+    EXEC SQL END DECLARE SECTION;
+    int status, client_id;
+    char *client_type;
+
+    name = argv[0];
+    EXEC SQL SELECT owner, owners INTO :users_id, :list_id FROM filesys
+      WHERE label = :name;
+
+    if (sqlca.sqlerrd[2] != 1)
+      return(MR_PERM);
+    if (users_id == cl->users_id)
+      return(MR_SUCCESS);
+    if ((status = get_client(cl, &client_type, &client_id)) != MR_SUCCESS)
+      return(status);
+    status = find_member("LIST", list_id, client_type, client_id, 0);
+    if (status)
+      return(MR_SUCCESS);
+    else
+      return(MR_PERM);
+}
+
diff --git a/server/qfollow.dc b/server/qfollow.dc
new file mode 100644 (file)
index 0000000..fc13e1d
--- /dev/null
@@ -0,0 +1,1250 @@
+/*
+ *     $Source$
+ *     $Author$
+ *     $Header$
+ *
+ *     Copyright (C) 1987 by the Massachusetts Institute of Technology
+ *     For copying and distribution information, please see the file
+ *     <mit-copyright.h>.
+ *
+ */
+
+#ifndef lint
+static char *rcsid_qsupport_dc = "$Header$";
+#endif lint
+
+#include <mit-copyright.h>
+#include "query.h"
+#include "mr_server.h"
+#include <ctype.h>
+#ifdef GDSS
+#include "gdss.h"
+#endif /* GDSS */
+EXEC SQL INCLUDE sqlca;
+EXEC SQL INCLUDE sqlda;
+#include "qrtn.h"
+
+extern char *whoami, *strsave();
+extern int ingres_errno, mr_errcode;
+
+EXEC SQL BEGIN DECLARE SECTION;
+extern char stmt_buf[];
+EXEC SQL END DECLARE SECTION;
+
+EXEC SQL WHENEVER SQLERROR CALL ingerr;
+
+
+/* FOLLOWUP ROUTINES */
+
+/* generic set_modtime routine.  This takes the table name from the query,
+ * and will update the modtime, modby, and modwho fields in the entry in
+ * the table whose name field matches argv[0].
+ */
+
+set_modtime(q, argv, cl)
+    struct query *q;
+    char *argv[];
+    client *cl;
+{
+    char *name, *entity, *table;
+    int who;
+
+    entity = cl->entity;
+    who = cl->client_id;
+    table = q->rtable;
+    name = argv[0];
+
+    sprintf(stmt_buf,"UPDATE %s SET modtime = 'now', modby = %d, modwith = '%s' WHERE %s.name = LEFT('%s',SIZE(%s.name))",table,who,entity,table,name,table);
+    EXEC SQL EXECUTE IMMEDIATE :stmt_buf;
+
+    return(MR_SUCCESS);
+}
+
+/* generic set_modtime_by_id routine.  This takes the table name from
+ * the query, and the id name from the validate record,
+ * and will update the modtime, modby, and modwho fields in the entry in
+ * the table whose id matches argv[0].
+ */
+
+set_modtime_by_id(q, argv, cl)
+    struct query *q;
+    char **argv;
+    client *cl;
+{
+    char *entity, *table, *id_name;
+    int who, id;
+
+    entity = cl->entity;
+    who = cl->client_id;
+    table = q->rtable;
+    id_name = q->validate->object_id;
+
+    id = *(int *)argv[0];
+    sprintf(stmt_buf,"UPDATE %s SET modtime = 'now', modby = %d, \
+modwith = '%s' WHERE %s.%s = %d",table,who,entity,table,id_name,id);
+    EXEC SQL EXECUTE IMMEDIATE :stmt_buf;
+    return(MR_SUCCESS);
+}
+
+
+/* Sets the finger modtime on a user record.  The users_id will be in argv[0].
+ */
+
+set_finger_modtime(q, argv, cl)
+    struct query *q;
+    char *argv[];
+    client *cl;
+{
+    EXEC SQL BEGIN DECLARE SECTION;
+    int users_id, who;
+    char *entity;
+    EXEC SQL END DECLARE SECTION;
+
+    entity = cl->entity;
+    who = cl->client_id;
+    users_id = *(int *)argv[0];
+
+    EXEC SQL UPDATE users SET fmodtime='now', fmodby = :who, fmodwith = :entity
+      WHERE users.users_id = :users_id;
+
+   return(MR_SUCCESS);
+}
+
+
+/* Sets the pobox modtime on a user record.  The users_id will be in argv[0].
+ */
+
+set_pobox_modtime(q, argv, cl)
+    struct query *q;
+    char **argv;
+    client *cl;
+{
+    EXEC SQL BEGIN DECLARE SECTION;
+    int users_id, who;
+    char *entity;
+    EXEC SQL END DECLARE SECTION;
+
+    entity = cl->entity;
+    who = cl->client_id;
+    users_id = *(int *)argv[0];
+
+    EXEC SQL UPDATE users SET pmodtime='now', pmodby = :who, pmodwith = :entity
+      WHERE users.users_id = :users_id;
+
+    return(MR_SUCCESS);
+}
+
+
+/* Like set_modtime, but uppercases the name first.
+ */
+
+set_uppercase_modtime(q, argv, cl)
+    struct query *q;
+    char **argv;
+    client *cl;
+{
+    char *name, *entity, *table;
+    int who;
+
+    entity = cl->entity;
+    who = cl->client_id;
+    table = q->rtable;
+    name = argv[0];
+
+    sprintf(stmt_buf,"UPDATE %s SET modtime = 'now', modby = %d, modwith = '%s' WHERE %s.name = UPPERCASE(LEFT('%s',SIZE(%s.name)))",table,who,entity,table,name,table);
+    EXEC SQL EXECUTE IMMEDIATE :stmt_buf;
+
+    return(MR_SUCCESS);
+}
+
+
+/* Sets the modtime on the machine whose mach_id is in argv[0].  This routine
+ * is necessary for add_machine_to_cluster becuase the table that query
+ * operates on is "mcm", not "machine".
+ */
+
+set_mach_modtime_by_id(q, argv, cl)
+    struct query *q;
+    char **argv;
+    client *cl;
+{
+    EXEC SQL BEGIN DECLARE SECTION;
+    char *entity;
+    int who, id;
+    EXEC SQL END DECLARE SECTION;
+
+    entity = cl->entity;
+    who = cl->client_id;
+    id = *(int *)argv[0];
+    EXEC SQL UPDATE machine SET modtime='now', modby = :who, modwith = :entity
+      WHERE machine.mach_id = :id;
+
+    return(MR_SUCCESS);
+}
+
+
+/* Sets the modtime on the cluster whose mach_id is in argv[0].  This routine
+ * is necessary for add_cluster_data and delete_cluster_data becuase the
+ * table that query operates on is "svc", not "cluster".
+ */
+
+set_cluster_modtime_by_id(q, argv, cl)
+    struct query *q;
+    char **argv;
+    client *cl;
+{
+    EXEC SQL BEGIN DECLARE SECTION;
+    char *entity;
+    int who, id;
+    EXEC SQL END DECLARE SECTION;
+
+    entity = cl->entity;
+    who = cl->client_id;
+
+    id = *(int *)argv[0];
+    EXEC SQL UPDATE cluster SET modtime='now', modby = :who, modwith = :entity
+      WHERE cluster.clu_id = :id;
+    return(MR_SUCCESS);
+}
+
+
+/* sets the modtime on the serverhost where the service name is in argv[0]
+ * and the mach_id is in argv[1].
+ */
+
+set_serverhost_modtime(q, argv, cl)
+    struct query *q;
+    char **argv;
+    client *cl;
+{
+    EXEC SQL BEGIN DECLARE SECTION;
+    char *entity, *serv;
+    int who, id;
+    EXEC SQL END DECLARE SECTION;
+
+    entity = cl->entity;
+    who = cl->client_id;
+
+    serv = argv[0];
+    id = *(int *)argv[1];
+    EXEC SQL UPDATE serverhosts
+      SET modtime = 'now', modby = :who, modwith = :entity
+      WHERE service = :serv AND mach_id = :id;
+    return(MR_SUCCESS);
+}
+
+
+/* sets the modtime on the nfsphys where the mach_id is in argv[0] and the
+ * directory name is in argv[1].
+ */
+
+set_nfsphys_modtime(q, argv, cl)
+    struct query *q;
+    char **argv;
+    client *cl;
+{
+    EXEC SQL BEGIN DECLARE SECTION;
+    char *entity, *dir;
+    int who, id;
+    EXEC SQL END DECLARE SECTION;
+
+    entity = cl->entity;
+    who = cl->client_id;
+
+    id = *(int *)argv[0];
+    dir = argv[1];
+    EXEC SQL UPDATE nfsphys SET modtime = 'now', modby = :who, modwith = :entity
+      WHERE dir = :dir AND mach_id = :id;
+    return(MR_SUCCESS);
+}
+
+
+/* sets the modtime on a filesystem, where argv[0] contains the filesys
+ * label.
+ */
+
+set_filesys_modtime(q, argv, cl)
+    struct query *q;
+    char *argv[];
+    client *cl;
+{
+    EXEC SQL BEGIN DECLARE SECTION;
+    char *label, *entity;
+    int who;
+    extern int _var_phys_id;
+
+    EXEC SQL END DECLARE SECTION;
+
+    entity = cl->entity;
+    who = cl->client_id;
+
+    label = argv[0];
+    if (!strcmp(q->shortname, "ufil"))
+      label = argv[1];
+
+    EXEC SQL UPDATE filesys SET modtime = 'now', modby = :who,
+        modwith = :entity, phys_id = :_var_phys_id
+      WHERE label = LEFT(:label,SIZE(label));
+    return(MR_SUCCESS);
+}
+
+
+/* sets the modtime on a zephyr class, where argv[0] contains the class
+ * name.
+ */
+
+set_zephyr_modtime(q, argv, cl)
+    struct query *q;
+    char *argv[];
+    client *cl;
+{
+    EXEC SQL BEGIN DECLARE SECTION;
+    char *class, *entity;
+    int who;
+    EXEC SQL END DECLARE SECTION;
+
+    entity = cl->entity;
+    who = cl->client_id;
+
+    class = argv[0];
+
+    EXEC SQL UPDATE zephyr SET modtime = 'now', modby = :who, modwith = :entity
+      WHERE class = LEFT(:class,SIZE(class));
+
+    return(MR_SUCCESS);
+}
+
+
+/* fixes the modby field.  This will be the second to last thing in the
+ * argv, the argv length is determined from the query structure.  It is
+ * passed as a pointer to an integer.  This will either turn it into a
+ * username, or # + the users_id.
+ */
+followup_fix_modby(q, sq, v, action, actarg, cl)
+    struct query *q;
+    register struct save_queue *sq;
+    struct validate *v;
+    register int (*action)();
+    register int actarg;
+    client *cl;
+{
+    register int i, j;
+    char **argv, *malloc();
+    int id, status;
+
+    i = q->vcnt - 2;
+    while (sq_get_data(sq, &argv)) {
+       id = atoi(argv[i]);
+       if (id > 0)
+         status = id_to_name(id, "USER", &argv[i]);
+       else
+         status = id_to_name(-id, "STRING", &argv[i]);
+       if (status && status != MR_NO_MATCH)
+         return(status);
+       (*action)(q->vcnt, argv, actarg);
+       for (j = 0; j < q->vcnt; j++)
+         free(argv[j]);
+       free(argv);
+    }
+    sq_destroy(sq);
+    return(MR_SUCCESS);
+}
+
+
+/* After retrieving a user account, fix the modby field and signature.
+ * The modby field is the second to last thing in the
+ * argv, the argv length is determined from the query structure.  It is
+ * passed as a pointer to an integer.  This will either turn it into a
+ * username, or # + the users_id.  Only "gua*" queries have a signature,
+ * these are ones with U_END return values.  "gub*" queries also use this
+ * routine but don't have a signature.
+ */
+followup_guax(q, sq, v, action, actarg, cl)
+    struct query *q;
+    register struct save_queue *sq;
+    struct validate *v;
+    register int (*action)();
+    register int actarg;
+    client *cl;
+{
+    register int i, j;
+    char **argv, *malloc();
+#ifdef GDSS
+    unsigned char sigbuf[256];
+    char *kname;
+    SigInfo  si;
+    EXEC SQL BEGIN DECLARE SECTION; 
+    int timestamp, who;
+    char *login;
+    varchar struct { short data_size; char data_buf[257];} rsig;
+    EXEC SQL END DECLARE SECTION; 
+#endif /* GDSS */
+    int id, status;
+
+    i = q->vcnt - 2;
+    while (sq_get_data(sq, &argv)) {
+#ifdef DEBUG
+       com_err(whoami, 0, "argv[SIGNATURE] = \"%s\"", argv[U_SIGNATURE]);
+#endif /* DEBUG */
+       id = atoi(argv[i]);
+       if (id > 0)
+         status = id_to_name(id, "USER", &argv[i]);
+       else
+         status = id_to_name(-id, "STRING", &argv[i]);
+       if (status && status != MR_NO_MATCH)
+         return(status);
+#ifdef GDSS
+       if (q->vcnt == U_END && strlen(argv[U_SIGNATURE])) {
+           login = argv[U_NAME];
+           EXEC SQL REPEATED SELECT signature, sigdate, sigwho 
+             INTO :rsig, :timestamp, :who FROM users
+             WHERE login = :login;
+           /** What about (INGRES) error handling? **/
+           kname = malloc(1);
+            status = id_to_name(who, "STRING", &kname);
+            si.timestamp = timestamp;
+           si.SigInfoVersion = 0; /* XXXXX this isn't used */
+           kname_parse(si.pname, si.pinst, si.prealm, kname);
+            free(kname);
+           rsig.data_buf[rsig.data_size] = 0;
+            si.rawsig = (unsigned char *)strsave(rsig.data_buf);
+           if (log_flags & LOG_GDSS)
+             com_err(whoami, 0, "rawsig length = %d, sig=\"%s\"", strlen(si.rawsig), si.rawsig);
+           GDSS_Recompose(&si, sigbuf);
+           free(si.rawsig);
+           free(argv[U_SIGNATURE]);
+           argv[U_SIGNATURE] = strsave(sigbuf);
+           if (log_flags & LOG_GDSS)
+             com_err(whoami, 0, "generated signature length %d", strlen(sigbuf));
+       }
+#endif /* GDSS */
+       (*action)(q->vcnt, argv, actarg);
+       for (j = 0; j < q->vcnt; j++)
+         free(argv[j]);
+       free(argv);
+    }
+    sq_destroy(sq);
+    return(MR_SUCCESS);
+}
+
+
+/**
+ ** followup_ausr - add finger and pobox entries, set_user_modtime
+ **
+ ** Inputs:
+ **    argv[0] - login (add_user)
+ **    argv[3] - last name
+ **    argv[4] - first name
+ **    argv[5] - middle name
+ **
+ **/
+
+followup_ausr(q, argv, cl)
+    struct query *q;
+    char *argv[];
+    client *cl;
+{
+    EXEC SQL BEGIN DECLARE SECTION;
+    int who, status, id;
+    char *login, *entity, *src, *dst, *name;
+    char fullname[129];
+    EXEC SQL END DECLARE SECTION;
+#ifdef GDSS
+    char databuf[32], *kname_unparse();
+    EXEC SQL BEGIN DECLARE SECTION;
+    char rawsig[128];
+    int sigwho, timestamp;
+    EXEC SQL END DECLARE SECTION;
+    SigInfo si;
+#endif /* GDSS */
+
+    /* build fullname */
+    if (strlen(argv[4]) && strlen(argv[5]))
+       sprintf(fullname, "%s %s %s", argv[4], argv[5], argv[3]);
+    else if (strlen(argv[4]))
+       sprintf(fullname, "%s %s", argv[4], argv[3]);
+    else
+       sprintf(fullname, "%s", argv[3]);
+
+#ifdef GDSS
+      if (q->vcnt == U_END && *argv[U_SIGNATURE]) {
+       /* unquote ' chars in signature */
+       for (dst = src = argv[U_SIGNATURE]; *src; ) {
+           if (*src == '\'')
+             src++;
+           *dst++ = *src++;
+       }
+       *dst = 0;
+        sprintf(databuf, "%s:%s", argv[U_NAME], argv[U_MITID]);
+        /* skip bytes for timestamp & kname */
+        si.rawsig = (unsigned char *) rawsig;
+        status = GDSS_Verify(databuf, strlen(databuf), argv[U_SIGNATURE], &si);
+       if (strlen(rawsig) > mr_sig_length) {
+           com_err(whoami, 0, "GDSS signature would be truncated.");  /** untested **/
+           return(MR_INTERNAL);
+       }
+        if (status == 0) {
+            name = kname_unparse(si.pname, si.pinst, si.prealm);
+            status = name_to_id(name, "STRING", &sigwho);
+            if (status == MR_NO_MATCH) {
+             sigwho=add_string(name);
+            } else if (status)
+              return(status);
+            timestamp = si.timestamp;
+        } else {
+           if (log_flags & LOG_GDSS)
+             hex_dump(argv[U_SIGNATURE]);
+           return(gdss2et(status));
+       }
+      } else {
+        rawsig[0] = 0;
+        sigwho = 0;
+        timestamp = 0;
+      }
+#endif /* GDSS */
+
+    login = argv[0];
+    who = cl->client_id;
+    entity = cl->entity;
+
+    /* create finger entry, pobox & set modtime on user */
+#ifdef GDSS
+    EXEC SQL REPEATED UPDATE users
+      SET modtime='now', modby=:who, modwith = :entity,
+          fullname = :fullname, affiliation = type,
+          signature = :rawsig, sigdate = :timestamp, sigwho = :sigwho,
+          fmodtime='now', fmodby = :who, fmodwith = :entity,
+          potype='NONE', pmodtime='now', pmodby = :who, pmodwith = :entity
+      WHERE login = :login;
+#else /* GDSS */
+    EXEC SQL REPEATED UPDATE users
+      SET modtime='now', modby=:who, modwith = :entity,
+          fullname = :fullname, affiliation = type,
+          fmodtime='now', fmodby = :who, fmodwith = :entity,
+          potype='NONE', pmodtime='now', pmodby = :who, pmodwith = :entity
+      WHERE login = :login;
+#endif /* GDSS */
+
+    return(MR_SUCCESS);
+}
+
+
+/**
+ ** followup_uusr - do signature, set_user_modtime
+ **
+ ** Inputs:
+ **   argv[0] - login (add_user)
+ **   argv[U_SIGNATURE] - sig
+ **
+ **/
+
+followup_uuac(q, argv, cl)
+    struct query *q;
+    char *argv[];
+    client *cl;
+{
+    EXEC SQL BEGIN DECLARE SECTION; 
+    int who, status, id;
+    char *entity, *name, *src, *dst;
+    EXEC SQL END DECLARE SECTION; 
+#ifdef GDSS
+    char databuf[32], *kname_unparse();
+    EXEC SQL BEGIN DECLARE SECTION; 
+    char rawsig[128];
+    char *login;
+    int sigwho, timestamp;
+    EXEC SQL END DECLARE SECTION; 
+    SigInfo si;
+#endif /* GDSS */
+    
+    id = *(int *)argv[0];
+    who = cl->client_id;
+    entity = cl->entity;
+    
+#ifdef GDSS
+    if (q->vcnt == U_MODTIME && *argv[U_SIGNATURE + 1]) {
+       /* unquote ' chars in signature */
+       for (dst = src = argv[U_SIGNATURE+1]; *src; ) {
+           if (*src == '\'')
+             src++;
+           *dst++ = *src++;
+       }
+       *dst = 0;
+        login = malloc(1);
+        status = id_to_name(id, "USER", &login);
+        sprintf(databuf, "%s:%s", login, argv[U_MITID+1]);
+        free(login);
+        /* skip bytes for timestamp & kname */
+        si.rawsig = (unsigned char *) rawsig;
+#ifdef DEBUG
+       com_err(whoami, 0, "verifying sig");
+#endif /* DEBUG */
+        status = GDSS_Verify(databuf, strlen(databuf), argv[U_SIGNATURE+1], &si);
+#ifdef DEBUG
+       com_err(whoami, 0, "verified");
+#endif /* DEBUG */
+       if (strlen(rawsig) > mr_sig_length) {
+           com_err(whoami, 0, "GDSS signature would be truncated.");  /** untested **/
+           return(MR_INTERNAL);
+       }
+        if (status == 0) {
+            name = kname_unparse(si.pname, si.pinst, si.prealm);
+            status = name_to_id(name, "STRING", &sigwho);
+            if (status == MR_NO_MATCH) {
+             sigwho=add_string(name);
+            } else if (status)
+              return(status);
+            timestamp = si.timestamp;
+        } else {
+           if (log_flags & LOG_GDSS)
+             hex_dump(argv[U_SIGNATURE+1]);
+           return(gdss2et(status));
+       }
+    } else {
+        rawsig[0] = 0;
+        sigwho = 0;
+        timestamp = 0;
+    }
+#endif /* GDSS */
+    /* create finger entry, pobox & set modtime on user */
+
+#ifdef GDSS
+    EXEC SQL REPEATED UPDATE users SET modtime='now', modby = :who, modwith = :entity,
+        signature = :rawsig, sigdate = :timestamp, sigwho = :sigwho
+      WHERE users_id = :id;
+#else /* GDSS */
+    EXEC SQL REPEATED UPDATE users SET modtime='now', modby = :who, modwith = :entity
+      WHERE users_id = :id;
+#endif /* GDSS */
+    return(MR_SUCCESS);
+}
+/* followup_gpob: fixes argv[2] based on the IDs currently there and the
+ * type in argv[1].  Then completes the upcall to the user.
+ *
+ * argv[2] is of the form "123:234" where the first integer is the machine
+ * ID if it is a pop box, and the second is the string ID if it is an SMTP
+ * box.  argv[1] should be "POP", "SMTP", or "NONE".  Boxes of type NONE
+ * are skipped.
+ */
+
+followup_gpob(q, sq, v, action, actarg, cl)
+    register struct query *q;
+    register struct save_queue *sq;
+    register struct validate *v;
+    register int (*action)();
+    int actarg;
+    client *cl;
+{
+    char **argv, *index();
+    char *ptype, *p;
+    int mid, sid, status, i;
+
+    /* for each row */
+    while (sq_get_data(sq, &argv)) {
+       mr_trim_args(2, argv);
+       ptype = argv[1];
+       p = index(argv[2], ':');
+       *p++ = 0;
+       mid = atoi(argv[2]);
+       sid = atoi(p);
+
+       if (!strcmp(ptype, "POP")) {
+           status = id_to_name(mid, "MACHINE", &argv[2]);
+           if (status == MR_NO_MATCH)
+             return(MR_MACHINE);
+       } else if (!strcmp(ptype, "SMTP")) {
+           status = id_to_name(sid, "STRING", &argv[2]);
+           if (status == MR_NO_MATCH)
+             return(MR_STRING);
+       } else /* ptype == "NONE" */ {
+           goto skip;
+       }
+       if (status) return(status);
+
+       if (!strcmp(q->shortname, "gpob")) {
+           sid = atoi(argv[4]);
+           if (sid > 0)
+             status = id_to_name(sid, "USER", &argv[4]);
+           else
+             status = id_to_name(-sid, "STRING", &argv[4]);
+       }
+       if (status && status != MR_NO_MATCH) return(status);
+
+       (*action)(q->vcnt, argv, actarg);
+    skip:
+       /* free saved data */
+       for (i = 0; i < q->vcnt; i++)
+           free(argv[i]);
+       free(argv);
+    }
+
+    sq_destroy(sq);
+    return (MR_SUCCESS);
+}
+
+
+/* followup_gsnt: fix the ace_name in argv[7].  argv[6] will contain the
+ * ace_type: "LIST", "USER", or "NONE".  Decode the id in argv[7] into the
+ * proper name based on the type, and repace that string in the argv.
+ * Also fixes the modby field by called followup_fix_modby.
+ */
+
+followup_gsnt(q, sq, v, action, actarg, cl)
+    register struct query *q;
+    register struct save_queue *sq;
+    register struct validate *v;
+    register int (*action)();
+    int actarg;
+    client *cl;
+{
+    char **argv, *malloc(), *realloc(), *type;
+    int id, i, idx, status;
+
+    idx = 7;
+
+    while (sq_get_data(sq, &argv)) {
+       mr_trim_args(q->vcnt, argv);
+
+       id = atoi(argv[i = q->vcnt - 2]);
+       if (id > 0)
+         status = id_to_name(id, "USER", &argv[i]);
+       else
+         status = id_to_name(-id, "STRING", &argv[i]);
+       if (status && status != MR_NO_MATCH)
+         return(status);
+
+       id = atoi(argv[idx]);
+       type = argv[idx - 1];
+
+       if (!strcmp(type, "LIST")) {
+           status = id_to_name(id, "LIST", &argv[idx]);
+       } else if (!strcmp(type, "USER")) {
+           status = id_to_name(id, "USER", &argv[idx]);
+       } else if (!strcmp(type, "KERBEROS")) {
+           status = id_to_name(id, "STRING", &argv[idx]);
+       } else if (!strcmp(type, "NONE")) {
+           status = 0;
+           free(argv[idx]);
+           argv[idx] = strsave("NONE");
+       } else {
+           status = 0;
+           free(argv[idx]);
+           argv[idx] = strsave("???");
+       }
+       if (status && status != MR_NO_MATCH)
+         return(status);
+
+       /* send the data */
+       (*action)(q->vcnt, argv, actarg);
+
+       /* free saved data */
+       for (i = 0; i < q->vcnt; i++)
+           free(argv[i]);
+       free(argv);
+    }
+
+    sq_destroy(sq);
+    return (MR_SUCCESS);
+}
+
+
+/* followup_glin: fix the ace_name in argv[8].  argv[7] will contain the
+ * ace_type: "LIST", "USER", or "NONE".  Decode the id in argv[8] into the
+ * proper name based on the type, and repace that string in the argv.
+ * Also fixes the modby field by called followup_fix_modby.
+ */
+
+followup_glin(q, sq, v, action, actarg, cl)
+    register struct query *q;
+    register struct save_queue *sq;
+    register struct validate *v;
+    register int (*action)();
+    int actarg;
+    client *cl;
+{
+    char **argv, *malloc(), *realloc(), *type;
+    int id, i, idx, status;
+
+    idx = 8;
+    if (!strcmp(q->shortname, "gsin"))
+      idx = 12;
+
+    while (sq_get_data(sq, &argv)) {
+       mr_trim_args(q->vcnt, argv);
+
+       id = atoi(argv[i = q->vcnt - 2]);
+       if (id > 0)
+         status = id_to_name(id, "USER", &argv[i]);
+       else
+         status = id_to_name(-id, "STRING", &argv[i]);
+       if (status && status != MR_NO_MATCH)
+         return(status);
+
+       id = atoi(argv[idx]);
+       type = argv[idx - 1];
+
+       if (!strcmp(type, "LIST")) {
+           status = id_to_name(id, "LIST", &argv[idx]);
+       } else if (!strcmp(type, "USER")) {
+           status = id_to_name(id, "USER", &argv[idx]);
+       } else if (!strcmp(type, "KERBEROS")) {
+           status = id_to_name(id, "STRING", &argv[idx]);
+       } else if (!strcmp(type, "NONE")) {
+           status = 0;
+           free(argv[idx]);
+           argv[idx] = strsave("NONE");
+       } else {
+           status = 0;
+           free(argv[idx]);
+           argv[idx] = strsave("???");
+       }
+       if (status && status != MR_NO_MATCH)
+         return(status);
+
+       if (!strcmp(q->shortname, "glin") && atoi(argv[6]) == -1) {
+           argv[6] = realloc(argv[6], strlen(UNIQUE_GID) + 1);
+           strcpy(argv[6], UNIQUE_GID);
+       }
+
+       /* send the data */
+       (*action)(q->vcnt, argv, actarg);
+
+       /* free saved data */
+       for (i = 0; i < q->vcnt; i++)
+           free(argv[i]);
+       free(argv);
+    }
+
+    sq_destroy(sq);
+    return (MR_SUCCESS);
+}
+
+
+/* followup_gqot: Fix the entity name, directory name & modby fields
+ *   argv[0] = filsys_id
+ *   argv[1] = type
+ *   argv[2] = entity_id
+ *   argv[3] = ascii(quota)
+ */
+
+followup_gqot(q, sq, v, action, actarg, cl)
+    struct query *q;
+    register struct save_queue *sq;
+    struct validate *v;
+    register int (*action)();
+    register int actarg;
+    client *cl;
+{
+    register int j;
+    char **argv, *malloc();
+    EXEC SQL BEGIN DECLARE SECTION;
+    int id;
+    char *name, *label;
+    EXEC SQL END DECLARE SECTION;
+    int status, idx;
+
+    if (!strcmp(q->name, "get_quota") ||
+       !strcmp(q->name, "get_quota_by_filesys"))
+      idx = 4;
+    else
+      idx = 3;
+    while (sq_get_data(sq, &argv)) {
+       if (idx == 4) {
+           switch (argv[1][0]) {
+           case 'U':
+               status = id_to_name(atoi(argv[2]), "USER", &argv[2]);
+               break;
+           case 'G':
+           case 'L':
+               status = id_to_name(atoi(argv[2]), "LIST", &argv[2]);
+               break;
+           case 'A':
+               free(argv[2]);
+               argv[2] = strsave("system:anyuser");
+               break;
+           default:
+               id = atoi(argv[2]);
+               argv[2] = malloc(8);
+               sprintf(argv[2], "%d", id);
+           }
+       }
+       id = atoi(argv[idx]);
+       free(argv[idx]);
+       argv[idx] = malloc(256);
+       name = argv[idx];
+       if (id == 0) {
+           label = argv[0];
+           EXEC SQL REPEATED SELECT name INTO :name FROM filesys
+             WHERE label = :label;
+       } else {
+           EXEC SQL REPEATED SELECT dir INTO :name FROM nfsphys
+             WHERE nfsphys_id = :id;
+       }
+       if (sqlca.sqlerrd[2] != 1) {
+           sprintf(argv[idx], "#%d", id);
+       }
+
+       id = atoi(argv[idx+3]);
+       if (id > 0)
+         status = id_to_name(id, "USER", &argv[idx+3]);
+       else
+         status = id_to_name(-id, "STRING", &argv[idx+3]);
+       if (status && status != MR_NO_MATCH)
+         return(status);
+       (*action)(q->vcnt, argv, actarg);
+       for (j = 0; j < q->vcnt; j++)
+         free(argv[j]);
+       free(argv);
+    }
+    sq_destroy(sq);
+    return(MR_SUCCESS);
+}
+
+
+/* followup_aqot: Add allocation to nfsphys after creating quota.
+ *   argv[0] = filsys_id
+ *   argv[1] = type if "add_quota" or "update_quota"
+ *   argv[2 or 1] = id
+ *   argv[3 or 2] = ascii(quota)
+ */
+
+followup_aqot(q, argv, cl)
+    struct query  *q;
+    char **argv;
+    client *cl;
+{
+    EXEC SQL BEGIN DECLARE SECTION;
+    int quota, id, fs, who, physid;
+    char *entity, *qtype, *table_name;
+    EXEC SQL END DECLARE SECTION;
+    char incr_qual[60];
+    char *incr_argv[2];
+    int status;
+
+    table_name=q->rtable;
+    fs = *(int *)argv[0];
+    EXEC SQL REPEATED SELECT phys_id INTO :physid FROM filesys
+      WHERE filsys_id = :fs;
+    if(ingres_errno)
+       return(mr_errcode);
+
+    if (!strcmp(q->shortname, "aqot") || !strcmp(q->shortname, "uqot")) {
+       qtype = argv[1];
+       id = *(int *)argv[2];
+       quota = atoi(argv[3]);
+       sprintf(incr_qual,"q.filsys_id = %d",fs);
+    } else {
+       qtype = "USER";
+       id = *(int *)argv[1];
+       quota = atoi(argv[2]);
+       sprintf(incr_qual,"q.filsys_id=%d AND q.type='%s' AND q.entity_id=%d",
+               fs,qtype,id);
+    }
+
+    /* quota case of incremental_{before|after} only looks at slot 1 */
+    incr_argv[1]=qtype;
+
+    /* Follows one of many possible gross hacks to fix these particular
+     * conflicts between what is possible in the query table and what 
+     * is possible in SQL.    
+     */
+    if(q->type==APPEND) {
+       incremental_clear_before();
+       EXEC SQL INSERT INTO quota 
+           (filsys_id, type, entity_id, quota, phys_id) 
+         VALUES (:fs, :qtype, :id, :quota, :physid);
+       incremental_after(table_name, incr_qual, incr_argv);    
+    } else {
+       incremental_before(table_name, incr_qual, incr_argv);
+       EXEC SQL UPDATE quota SET quota = :quota
+         WHERE filsys_id = :fs AND type = :qtype AND entity_id = :id;
+       status = mr_errcode;
+       incremental_after(table_name, incr_qual, incr_argv);
+    }
+
+    if (ingres_errno)
+       return(mr_errcode);
+    flush_name(argv[0], q->rtable);  
+    if(q->type==APPEND) {
+        EXEC SQL UPDATE tblstats SET appends = appends + 1, modtime = 'now'
+         WHERE table_name = :table_name;
+    } else {
+        EXEC SQL UPDATE tblstats SET updates = updates + 1, modtime = 'now'
+         WHERE table_name = :table_name;
+    }
+       
+    /* Proceed with original followup */
+    who = cl->client_id;
+    entity = cl->entity;
+
+    EXEC SQL REPEATED UPDATE quota
+      SET modtime = 'now', modby = :who, modwith = :entity
+      WHERE filsys_id = :fs and type = :qtype and entity_id = :id;
+    EXEC SQL REPEATED UPDATE nfsphys SET allocated = allocated + :quota
+      WHERE nfsphys_id = :physid;
+    if (ingres_errno) return(mr_errcode);
+    return(MR_SUCCESS);
+}
+
+
+/* Necessitated by the requirement of a correlation name by the incremental
+ * routines, since query table deletes don't provide one. 
+ */
+followup_dqot(q,argv,cl)
+    struct query *q;
+    char **argv;
+    struct client *cl;
+{
+    char *qtype;
+    int id, fs;
+    char *incr_argv[2];
+    EXEC SQL BEGIN DECLARE SECTION; 
+    char incr_qual[80];
+    char *tblname;
+    EXEC SQL END DECLARE SECTION; 
+
+    fs = *(int *)argv[0];
+    if (!strcmp(q->shortname, "dqot")) {
+       qtype = argv[1];
+       id = *(int *)argv[2];
+    } else {
+       qtype = "USER";
+       id = *(int *)argv[1];
+    }
+    sprintf(incr_qual,"q.filsys_id=%d AND q.type='%s' AND q.entity_id=%d",
+           fs,qtype,id);
+
+    /* quota case of incremental_{before|after} only looks at slot 1 */
+    incr_argv[1]=qtype;
+
+    incremental_before(q->rtable, incr_qual, incr_argv);
+    EXEC SQL DELETE FROM quota q WHERE :incr_qual;
+    incremental_clear_after();
+
+    if (ingres_errno) 
+       return(mr_errcode);
+    flush_name(argv[0], q->rtable);
+
+    tblname = q->rtable;
+    EXEC SQL UPDATE tblstats SET deletes = deletes + 1, modtime = 'now'
+      WHERE table_name = :tblname;
+    return(MR_SUCCESS);
+}
+
+
+followup_gpce(q, sq, v, action, actarg, cl)
+    struct query *q;
+    register struct save_queue *sq;
+    struct validate *v;
+    register int (*action)();
+    register int actarg;
+    client *cl;
+{
+    register int i, j;
+    char **argv, *malloc();
+    int id, status;
+
+    i = q->vcnt - 2;
+    while (sq_get_data(sq, &argv)) {
+       id = atoi(argv[PCAP_QSERVER]);
+       status = id_to_name(id, "MACHINE", &argv[PCAP_QSERVER]);
+       if (status) return (status);
+       id = atoi(argv[i]);
+       if (id > 0)
+         status = id_to_name(id, "USER", &argv[i]);
+       else
+         status = id_to_name(-id, "STRING", &argv[i]);
+       if (status && status != MR_NO_MATCH)
+         return(status);
+       (*action)(q->vcnt, argv, actarg);
+       for (j = 0; j < q->vcnt; j++)
+         free(argv[j]);
+       free(argv);
+    }
+    sq_destroy(sq);
+    return(MR_SUCCESS);
+}
+
+
+/* followup_gzcl:
+ */
+
+followup_gzcl(q, sq, v, action, actarg, cl)
+    register struct query *q;
+    register struct save_queue *sq;
+    register struct validate *v;
+    register int (*action)();
+    int actarg;
+    client *cl;
+{
+    int id, i, status;
+    char **argv;
+
+    while (sq_get_data(sq, &argv)) {
+       mr_trim_args(q->vcnt, argv);
+
+       id = atoi(argv[i = q->vcnt - 2]);
+       if (id > 0)
+         status = id_to_name(id, "USER", &argv[i]);
+       else
+         status = id_to_name(-id, "STRING", &argv[i]);
+       if (status && status != MR_NO_MATCH)
+         return(status);
+
+       for (i = 1; i < 8; i+=2) {
+           id = atoi(argv[i+1]);
+           if (!strcmp(argv[i], "LIST")) {
+               status = id_to_name(id, "LIST", &argv[i+1]);
+           } else if (!strcmp(argv[i], "USER")) {
+               status = id_to_name(id, "USER", &argv[i+1]);
+           } else if (!strcmp(argv[i], "KERBEROS")) {
+               status = id_to_name(id, "STRING", &argv[i+1]);
+           } else if (!strcmp(argv[i], "NONE")) {
+               status = 0;
+               free(argv[i+1]);
+               argv[i+1] = strsave("NONE");
+           } else {
+               status = 0;
+               free(argv[i+1]);
+               argv[i+1] = strsave("???");
+           }
+           if (status && status != MR_NO_MATCH)
+             return(status);
+       }
+
+       /* send the data */
+       (*action)(q->vcnt, argv, actarg);
+
+       /* free saved data */
+       for (i = 0; i < q->vcnt; i++)
+           free(argv[i]);
+       free(argv);
+    }
+    sq_destroy(sq);
+    return(MR_SUCCESS);
+}
+
+
+/* followup_gsha:
+ */
+
+followup_gsha(q, sq, v, action, actarg, cl)
+    register struct query *q;
+    register struct save_queue *sq;
+    register struct validate *v;
+    register int (*action)();
+    int actarg;
+    client *cl;
+{
+    char **argv;
+    int i, id, status;
+
+    while (sq_get_data(sq, &argv)) {
+       mr_trim_args(q->vcnt, argv);
+
+       id = atoi(argv[4]);
+       if (id > 0)
+         status = id_to_name(id, "USER", &argv[4]);
+       else
+         status = id_to_name(-id, "STRING", &argv[4]);
+       if (status && status != MR_NO_MATCH)
+         return(status);
+
+       id = atoi(argv[2]);
+       if (!strcmp(argv[1], "LIST")) {
+           status = id_to_name(id, "LIST", &argv[2]);
+       } else if (!strcmp(argv[1], "USER")) {
+           status = id_to_name(id, "USER", &argv[2]);
+       } else if (!strcmp(argv[1], "KERBEROS")) {
+           status = id_to_name(id, "STRING", &argv[2]);
+       } else if (!strcmp(argv[1], "NONE")) {
+           status = 0;
+           free(argv[2]);
+           argv[2] = strsave("NONE");
+       } else {
+           status = 0;
+           free(argv[2]);
+           argv[2] = strsave("???");
+       }
+       if (status && status != MR_NO_MATCH)
+         return(status);
+
+       /* send the data */
+       (*action)(q->vcnt, argv, actarg);
+
+       /* free saved data */
+       for (i = 0; i < q->vcnt; i++)
+           free(argv[i]);
+       free(argv);
+    }
+    sq_destroy(sq);
+    return(MR_SUCCESS);
+}
+
+
+int _sdl_followup(q, argv, cl)
+    struct query *q;
+    char **argv;
+    client *cl;
+{
+    int i;
+    i = atoi(argv[0]);
+    log_flags = i;
+    if (i & LOG_SQL) {
+       EXEC SQL set printqry;
+    } else {
+       EXEC SQL set noprintqry;
+    }
+    return(MR_SUCCESS);
+}
+
+
+static hex_dump(p)
+unsigned  char *p;
+{
+    char buf[BUFSIZ];
+    int i;
+
+    fprintf(stderr, "Size: %d\n", strlen(p));
+    while (strlen(p) >= 8) {
+       fprintf(stderr, "%02x %02x %02x %02x %02x %02x %02x %02x\n",
+               p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
+       p += 8;
+    }
+    switch (strlen(p)) {
+    case 7:
+       fprintf(stderr, "%02x %02x %02x %02x %02x %02x %02x\n",
+               p[0], p[1], p[2], p[3], p[4], p[5], p[6]);
+       break;
+    case 6:
+       fprintf(stderr, "%02x %02x %02x %02x %02x %02x\n",
+               p[0], p[1], p[2], p[3], p[4], p[5]);
+       break;
+    case 5:
+       fprintf(stderr, "%02x %02x %02x %02x %02x\n",
+               p[0], p[1], p[2], p[3], p[4]);
+       break;
+    case 4:
+       fprintf(stderr, "%02x %02x %02x %02x\n",
+               p[0], p[1], p[2], p[3]);
+       break;
+    case 3:
+       fprintf(stderr, "%02x %02x %02x\n",
+               p[0], p[1], p[2]);
+       break;
+    case 2:
+       fprintf(stderr, "%02x %02x\n",
+               p[0], p[1]);
+       break;
+    case 1:
+       fprintf(stderr, "%02x\n",
+               p[0]);
+       break;
+    default:
+       return;
+    }
+}
+
diff --git a/server/qsetup.dc b/server/qsetup.dc
new file mode 100644 (file)
index 0000000..ff54b7c
--- /dev/null
@@ -0,0 +1,801 @@
+/*
+ *     $Source$
+ *     $Author$
+ *     $Header$
+ *
+ *     Copyright (C) 1987 by the Massachusetts Institute of Technology
+ *     For copying and distribution information, please see the file
+ *     <mit-copyright.h>.
+ *
+ */
+
+#ifndef lint
+static char *rcsid_qsupport_dc = "$Header$";
+#endif lint
+
+#include <mit-copyright.h>
+#include "query.h"
+#include "mr_server.h"
+#include <ctype.h>
+#ifdef GDSS
+#include "gdss.h"
+#endif /* GDSS */
+EXEC SQL INCLUDE sqlca;
+EXEC SQL INCLUDE sqlda;
+#include "qrtn.h"
+
+extern char *whoami, *strsave();
+extern int ingres_errno, mr_errcode;
+
+EXEC SQL BEGIN DECLARE SECTION;
+extern char stmt_buf[];
+EXEC SQL END DECLARE SECTION;
+
+EXEC SQL WHENEVER SQLERROR CALL ingerr;
+
+
+/* Setup Routines */
+
+/* Setup routine for add_user
+ *
+ * Inputs: argv[0] - login
+ *         argv[1] - uid
+ *
+ * Description:
+ *
+ * - if argv[1] == UNIQUE_UID then set argv[1] = next(uid)
+ * - if argv[0] == UNIQUE_LOGIN then set argv[0] = "#<uid>"
+ */
+
+setup_ausr(q, argv, cl)
+    struct query *q;
+    register char *argv[];
+    client *cl;
+{
+    int row;
+    EXEC SQL BEGIN DECLARE SECTION;
+    int nuid;
+    EXEC SQL END DECLARE SECTION;
+
+    if (!strcmp(q->shortname, "uusr") || !strcmp(q->shortname, "uuac"))
+      row = 2;
+    else
+      row = 1;
+    if (!strcmp(argv[row], UNIQUE_UID) || atoi(argv[row]) == -1) {
+       if (set_next_object_id("uid", "users", 1))
+         return(MR_INGRES_ERR);
+       EXEC SQL SELECT value INTO :nuid FROM numvalues WHERE name = 'uid';
+       if (sqlca.sqlerrd[2] != 1)
+         return(MR_INTERNAL);
+       sprintf(argv[row], "%d", nuid);
+    }
+
+    if (!strcmp(argv[0], UNIQUE_LOGIN) || atoi(argv[row]) == -1) {
+       sprintf(argv[0], "#%s", argv[row]);
+    }
+
+    if((mr_errcode=prefetch_value(q,argv,cl))!=MR_SUCCESS)
+      return(mr_errcode);
+
+    return(MR_SUCCESS);
+}
+
+
+/* setup_dusr - verify that the user is no longer being referenced
+ * and may safely be deleted.
+ */
+
+int setup_dusr(q, argv)
+    struct query *q;
+    char **argv;
+{
+    EXEC SQL BEGIN DECLARE SECTION;
+    int flag, id, cnt;
+    EXEC SQL END DECLARE SECTION;
+
+    id = *(int *)argv[0];
+
+    /* For now, only allow users to be deleted if their status is 0 */
+    EXEC SQL REPEATED SELECT status INTO :flag FROM users
+      WHERE users_id = :id;
+    if (flag != 0 && flag != 4)
+      return(MR_IN_USE);
+
+    EXEC SQL REPEATED DELETE FROM quota WHERE entity_id = :id AND type='USER';
+    EXEC SQL REPEATED DELETE FROM krbmap WHERE users_id = :id;
+    EXEC SQL REPEATED SELECT COUNT(member_id) INTO :cnt FROM imembers
+      WHERE member_id = :id AND member_type = 'USER';
+    if (cnt > 0)
+       return(MR_IN_USE);
+    EXEC SQL REPEATED SELECT COUNT(label) INTO :cnt FROM filesys 
+       WHERE owner = :id;
+    if (cnt > 0)
+       return(MR_IN_USE);
+    EXEC SQL REPEATED SELECT COUNT(name) INTO :cnt FROM list
+      WHERE acl_id = :id AND acl_type = 'USER';
+    if (cnt > 0)
+       return(MR_IN_USE);
+    EXEC SQL REPEATED SELECT COUNT(name) INTO :cnt FROM servers
+      WHERE acl_id = :id AND acl_type = 'USER';
+    if (cnt > 0)
+       return(MR_IN_USE);
+    EXEC SQL REPEATED SELECT COUNT(acl_id) INTO :cnt FROM hostaccess
+      WHERE acl_id = :id AND acl_type = 'USER';
+    if (cnt > 0)
+       return(MR_IN_USE);
+    if (ingres_errno)
+       return(mr_errcode);
+    return(MR_SUCCESS);
+}
+
+
+/* setup_spop: verify that there is already a valid POP machine_id in the
+ * pop_id field.  Also take care of keeping track of the post office usage.
+ */
+int setup_spop(q, argv)
+struct query *q;
+char **argv;
+{
+    EXEC SQL BEGIN DECLARE SECTION;
+    int id, mid, flag;
+    char type[9];
+    EXEC SQL END DECLARE SECTION;
+
+    id = *(int *)argv[0];
+    EXEC SQL REPEATED SELECT potype, pop_id INTO :type, :mid FROM users
+      WHERE users_id = :id;
+    if(sqlca.sqlerrd[2] = 0)
+      return(MR_MACHINE);
+    EXEC SQL REPEATED SELECT mach_id INTO :mid FROM machine
+      WHERE mach_id = :mid;
+    if (sqlca.sqlerrd[2] = 0)
+      return(MR_MACHINE);
+    if (strcmp(strtrim(type), "POP"))
+      set_pop_usage(mid, 1);
+    return(MR_SUCCESS);
+}
+
+
+/* setup_dpob:  Take care of keeping track of the post office usage.
+ */
+int setup_dpob(q, argv)
+     struct query *q;
+     char **argv;
+{
+    EXEC SQL BEGIN DECLARE SECTION;
+    int id, user;
+    char type[9];
+    EXEC SQL END DECLARE SECTION;
+
+    user = *(int *)argv[0];
+    EXEC SQL REPEATED SELECT potype, pop_id INTO :type, :id FROM users
+      WHERE users_id = :user;
+    if (ingres_errno) return(mr_errcode);
+
+    if (!strcmp(strtrim(type), "POP"))
+      set_pop_usage(id, -1);
+    return(MR_SUCCESS);
+}
+
+
+/* setup_dmac - verify that the machine is no longer being referenced
+ * and may safely be deleted.
+ */
+
+int setup_dmac(q, argv)
+    struct query *q;
+    char **argv;
+{
+    EXEC SQL BEGIN DECLARE SECTION;
+    int flag, id, cnt;
+    EXEC SQL END DECLARE SECTION;
+
+    id = *(int *)argv[0];
+    EXEC SQL REPEATED SELECT COUNT(login) INTO :cnt FROM users
+      WHERE potype='POP' AND pop_id = :id;
+    if (cnt > 0)
+       return(MR_IN_USE);
+    EXEC SQL REPEATED SELECT COUNT(mach_id) INTO :cnt FROM serverhosts
+      WHERE mach_id = :id;
+    if (cnt > 0)
+       return(MR_IN_USE);
+    EXEC SQL REPEATED SELECT COUNT(mach_id) INTO :cnt FROM nfsphys
+      WHERE mach_id = :id;
+    if (cnt > 0)
+       return(MR_IN_USE);
+    EXEC SQL REPEATED SELECT COUNT(mach_id) INTO :cnt FROM hostaccess
+      WHERE mach_id = :id;
+    if (cnt > 0)
+       return(MR_IN_USE);
+    EXEC SQL REPEATED SELECT COUNT(mach_id) INTO :cnt FROM printcap
+      WHERE mach_id = :id;
+    if (cnt > 0)
+       return(MR_IN_USE);
+    EXEC SQL REPEATED SELECT COUNT(quotaserver) INTO :cnt FROM printcap
+      WHERE quotaserver = :id;
+    if (cnt > 0)
+       return(MR_IN_USE);
+    EXEC SQL REPEATED SELECT COUNT(mach_id) INTO :cnt FROM palladium
+      WHERE mach_id = :id;
+    if (cnt > 0)
+       return(MR_IN_USE);
+
+    EXEC SQL REPEATED DELETE FROM mcmap WHERE mach_id = :id;
+    if (ingres_errno) return(mr_errcode);
+    return(MR_SUCCESS);
+}
+
+
+/* setup_dsnt - verify that the subnet is no longer being referenced
+ * and may safely be deleted.
+ */
+
+int setup_dsnt(q, argv)
+    struct query *q;
+    char **argv;
+{
+    EXEC SQL BEGIN DECLARE SECTION;
+    int flag, id, cnt = 0;
+    EXEC SQL END DECLARE SECTION;
+
+#ifdef notdef
+    id = *(int *)argv[0];
+    EXEC SQL REPEATED SELECT COUNT(mach_id) INTO :cnt FROM machine
+      WHERE snet_id = :id;
+    if (cnt > 0)
+       return(MR_IN_USE);
+#endif
+    return(MR_SUCCESS);
+}
+
+
+/* setup_dclu - verify that the cluster is no longer being referenced
+ * and may safely be deleted.
+ */
+
+int setup_dclu(q, argv)
+    struct query *q;
+    char **argv;
+{
+    EXEC SQL BEGIN DECLARE SECTION;
+    int id, cnt;
+    EXEC SQL END DECLARE SECTION;
+
+    id = *(int *)argv[0];
+    EXEC SQL REPEATED SELECT COUNT(mach_id) INTO :cnt FROM mcmap
+      WHERE clu_id = :id;
+    if (cnt > 0)
+       return(MR_IN_USE);
+    EXEC SQL REPEATED SELECT COUNT(clu_id) INTO :cnt FROM svc
+      WHERE clu_id = :id;
+    if (cnt > 0)
+       return(MR_IN_USE);
+    if (ingres_errno)
+       return(mr_errcode);
+    return(MR_SUCCESS);
+}
+
+
+/* setup_alis - if argv[5] is non-zero and argv[6] is UNIQUE_ID, then allocate
+ * a new gid and put it in argv[6].  Otherwise if argv[6] is UNIQUE_ID but
+ * argv[5] is not, then remember that UNIQUE_ID is being stored by putting
+ * a -1 there.  Remember that this is also used for ulis, with the indexes
+ * at 6 & 7.  Also check that the list name does not contain uppercase
+ * characters, control characters, @, or :.
+ */
+
+static int badlistchars[] = {
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^@ - ^O */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^P - ^_ */
+    1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, /* SPACE - / */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, /* 0 - ? */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* @ - O */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, /* P - _ */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ` - o */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* p - ^? */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+};
+
+int setup_alis(q, argv, cl)
+    struct query *q;
+    char *argv[];
+    client *cl;
+{
+    EXEC SQL BEGIN DECLARE SECTION;
+    int ngid;
+    EXEC SQL END DECLARE SECTION;
+    char *malloc();
+    unsigned char *p;
+    int idx;
+
+    if (!strcmp(q->shortname, "alis"))
+      idx = 0;
+    else if (!strcmp(q->shortname, "ulis"))
+      idx = 1;
+
+    for (p = (unsigned char *) argv[idx]; *p; p++)
+      if (badlistchars[*p])
+        return(MR_BAD_CHAR);
+    if (!strcmp(argv[6 + idx], UNIQUE_GID) || atoi(argv[6 + idx]) == -1) {
+       if (atoi(argv[5 + idx])) {
+           if (set_next_object_id("gid", "list", 1))
+             return(MR_INGRES_ERR);
+           EXEC SQL REPEATED SELECT value INTO :ngid FROM numvalues
+             WHERE name = 'gid';
+           if (ingres_errno) return(mr_errcode);
+           sprintf(argv[6 + idx], "%d", ngid);
+       } else {
+           strcpy(argv[6 + idx], "-1");
+       }
+    }
+
+    if((mr_errcode=prefetch_value(q,argv,cl))!=MR_SUCCESS)
+      return(mr_errcode);
+
+    return(MR_SUCCESS);
+}
+
+
+/* setup_dlis - verify that the list is no longer being referenced
+ * and may safely be deleted.
+ */
+
+int setup_dlis(q, argv)
+    struct query *q;
+    char *argv[];
+{
+    int flag, id, ec;
+
+    id = *(int *)argv[0];
+    sprintf(stmt_buf,"SELECT member_id FROM imembers WHERE member_id = %d AND member_type='LIST'",id);
+    if(ec=mr_select_any(stmt_buf)) {
+       if(ec==MR_EXISTS) return(MR_IN_USE); else return(ec);
+    }
+    
+    sprintf(stmt_buf,"SELECT member_id FROM imembers WHERE list_id = %d",id);
+    if(ec=mr_select_any(stmt_buf)) {
+       if(ec==MR_EXISTS) return(MR_IN_USE); else return(ec);
+    }
+
+    sprintf(stmt_buf,"SELECT label FROM filesys WHERE owners = %d",id);
+    if(ec=mr_select_any(stmt_buf)) {
+       if(ec==MR_EXISTS) return(MR_IN_USE); else return(ec);
+    }
+
+    sprintf(stmt_buf,"SELECT tag FROM capacls WHERE list_id = %d",id);
+    if(ec=mr_select_any(stmt_buf)) {
+       if(ec==MR_EXISTS) return(MR_IN_USE); else return(ec);
+    }
+
+    sprintf(stmt_buf,"SELECT name FROM list WHERE acl_id = %d AND acl_type='LIST' AND list_id != %d",id,id);
+    if(ec=mr_select_any(stmt_buf)) {
+       if(ec==MR_EXISTS) return(MR_IN_USE); else return(ec);
+    }
+
+    sprintf(stmt_buf,"SELECT name FROM servers WHERE acl_id = %d AND acl_type='LIST'",id);
+    if(ec=mr_select_any(stmt_buf)) {
+       if(ec==MR_EXISTS) return(MR_IN_USE); else return(ec);
+    }
+
+    sprintf(stmt_buf,"SELECT entity_id FROM quota WHERE entity_id = %d AND type='GROUP'",id);
+    if(ec=mr_select_any(stmt_buf)) {
+       if(ec==MR_EXISTS) return(MR_IN_USE); else return(ec);
+    }
+    sprintf(stmt_buf,"SELECT acl_id  FROM hostaccess WHERE acl_id = %d AND acl_type='LIST'",id);
+    if(ec=mr_select_any(stmt_buf)) {
+       if(ec==MR_EXISTS) return(MR_IN_USE); else return(ec);
+    }
+
+    sprintf(stmt_buf,"SELECT class FROM zephyr z \
+WHERE z.xmt_type = 'LIST' AND z.xmt_id = %d \
+OR z.sub_type = 'LIST' AND z.sub_id = %d \
+OR z.iws_type = 'LIST' AND z.iws_id = %d \
+OR z.iui_type = 'LIST' AND z.iui_id = %d",id,id,id,id);
+    if(ec=mr_select_any(stmt_buf)) {
+       if(ec==MR_EXISTS) return(MR_IN_USE); else return(ec);
+    }
+
+    return(MR_SUCCESS);
+}
+
+
+/* setup_dsin - verify that the service is no longer being referenced
+ * and may safely be deleted.
+ */
+
+int setup_dsin(q, argv)
+    struct query *q;
+    char **argv;
+{
+    EXEC SQL BEGIN DECLARE SECTION;     
+    int ec;
+    char *svrname;
+    EXEC SQL END DECLARE SECTION;
+
+    sprintf(stmt_buf,"SELECT service FROM serverhosts WHERE service = UPPERCASE('%s')",argv[0]);
+    if(ec=mr_select_any(stmt_buf)) {
+       if(ec==MR_EXISTS) 
+           return(MR_IN_USE); 
+       else 
+           return(ec);
+    }
+
+    svrname=argv[0];
+    EXEC SQL SELECT inprogress INTO :ec FROM servers 
+      WHERE name=UPPERCASE(:svrname);
+    if(ingres_errno) 
+       return(mr_errcode);
+    if(ec) 
+       return(MR_IN_USE); 
+
+    return(MR_SUCCESS);
+}
+
+
+/* setup_dshi - verify that the service-host is no longer being referenced
+ * and may safely be deleted.
+ */
+
+int setup_dshi(q, argv)
+    struct query *q;
+    char **argv;
+{
+    EXEC SQL BEGIN DECLARE SECTION;
+    int id, ec;
+    char *svrname;
+    EXEC SQL END DECLARE SECTION;
+
+    svrname=argv[0];
+    id = *(int *)argv[1];
+
+    EXEC SQL SELECT inprogress INTO :ec FROM serverhosts 
+      WHERE service=UPPERCASE(:svrname) AND mach_id = :id;
+    if(ingres_errno) 
+       return(mr_errcode);
+    if(ec) 
+       return(MR_IN_USE); 
+
+
+    return(MR_SUCCESS);
+}
+
+
+/**
+ ** setup_add_filesys - verify existance of referenced file systems
+ **
+ ** Inputs:     Add
+ **   argv[1] - type
+ **   argv[2] - mach_id
+ **   argv[3] - name
+ **   argv[5] - access
+ **
+ ** Description:
+ **   - for type = RVD:
+ **       * allow anything
+ **   - for type = NFS:
+ **        * extract directory prefix from name
+ **        * verify mach_id/dir in nfsphys
+ **        * verify access in {r, w, R, W}
+ **
+ **  Side effect: sets variable _var_phys_id to the ID of the physical
+ **    filesystem (nfsphys_id for NFS, 0 for RVD)
+ **
+ ** Errors:
+ **   MR_NFS - specified directory not exported
+ **   MR_FILESYS_ACCESS - invalid filesys access
+ **
+ **/
+
+EXEC SQL BEGIN DECLARE SECTION;
+int _var_phys_id;
+EXEC SQL END DECLARE SECTION;
+
+setup_afil(q, argv, cl)
+    struct query *q;
+    char *argv[];
+    client *cl;
+{
+    char *type, *name;
+    int mach_id;
+    EXEC SQL BEGIN DECLARE SECTION;
+    int ok;
+    char ftype[32], *access;
+    EXEC SQL END DECLARE SECTION;
+
+    type = argv[1];
+    mach_id = *(int *)argv[2];
+    name = argv[3];
+    access = argv[5];
+    _var_phys_id = 0;
+
+    sprintf(ftype, "fs_access_%s", type);
+    EXEC SQL SELECT COUNT(trans) INTO :ok FROM alias
+       WHERE name = :ftype AND type = 'TYPE' and trans = :access;   
+    if (ingres_errno) return(mr_errcode);
+    if (ok == 0) return(MR_FILESYS_ACCESS);
+
+    if((mr_errcode=prefetch_value(q,argv,cl))!=MR_SUCCESS)
+      return(mr_errcode);
+
+    if (!strcmp(type, "NFS"))
+       return (check_nfs(mach_id, name, access));
+
+    return(MR_SUCCESS);
+}
+
+
+/* Verify the arguments, depending on the FStype.  Also, if this is an
+ * NFS filesystem, then update any quotas for that filesystem to reflect
+ * the new phys_id.
+ */
+
+setup_ufil(q, argv, cl)
+    struct query *q;
+    char *argv[];
+    client *cl;
+{
+    int mach_id, status;
+    char *type, *name;
+    EXEC SQL BEGIN DECLARE SECTION;
+    int fid, total, who, ok;
+    char *entity, ftype[32], *access;
+    short int total_null;
+    EXEC SQL END DECLARE SECTION;
+
+    _var_phys_id = 0;
+    type = argv[2];
+    mach_id = *(int *)argv[3];
+    name = argv[4];
+    access = argv[6];
+    fid = *(int *)argv[0];
+    who = cl->client_id;
+    entity = cl->entity;
+
+    sprintf(ftype, "fs_access_%s", type);
+    EXEC SQL SELECT COUNT(trans) INTO :ok FROM alias
+      WHERE name = :ftype AND type='TYPE' AND trans = :access;
+    if (ingres_errno) return(mr_errcode);
+    if (ok == 0) return(MR_FILESYS_ACCESS);
+
+    EXEC SQL SELECT type INTO :ftype FROM filesys
+      WHERE filsys_id = :fid;
+    strtrim(ftype);
+    if (ingres_errno) return(mr_errcode);
+
+    if (!strcmp(type, "NFS")) {
+       status = check_nfs(mach_id, name, access);
+       EXEC SQL UPDATE quota SET phys_id = :_var_phys_id
+         WHERE filsys_id = :fid;
+       if (ingres_errno) return(mr_errcode);
+       return(status);
+    } else if (!strcmp(type, "AFS") && strcmp(ftype, "AFS")) {
+       total = 0;
+       EXEC SQL REPEATED DELETE FROM quota
+         WHERE type = 'ANY' AND filsys_id = :fid;
+       EXEC SQL SELECT SUM (quota) INTO :total:total_null FROM quota
+         WHERE filsys_id = :fid AND phys_id != 0;
+       if (ingres_errno) return(mr_errcode);
+       if (!total_null && (total != 0)) {
+/*
+ *             append quota (quota = total, filsys_id = fid,
+ *                          phys_id = 0, entity_id = 0, type = "ANY",
+ *                          modtime = "now", modby = who, modwith = entity)
+ */
+           EXEC SQL INSERT INTO quota (quota, filsys_id, phys_id, entity_id,
+                                       type, modtime, modby, modwith)
+             VALUES (:total, :fid, 0, 0,
+                     'ANY', 'now', :who, :entity) ;
+           if (ingres_errno) return(mr_errcode);
+       }
+    } else {
+       EXEC SQL UPDATE quota SET phys_id = 0 WHERE filsys_id = :fid;
+       if (ingres_errno) return(mr_errcode);
+    }
+    return(MR_SUCCESS);
+}
+
+
+/* Find the NFS physical partition that the named directory is on.
+ * This is done by comparing the dir against the mount point of the
+ * partition.  To make sure we get the correct match when there is
+ * more than one, we sort the query in reverse order by dir name.
+ */
+
+check_nfs(mach_id, name, access)
+    EXEC SQL BEGIN DECLARE SECTION;
+    int mach_id;
+    EXEC SQL END DECLARE SECTION;
+    char *name;
+    char *access;
+{
+    EXEC SQL BEGIN DECLARE SECTION;
+    char dir[81];
+    EXEC SQL END DECLARE SECTION;
+    char caccess;
+    register int status;
+    register char *cp1;
+    register char *cp2;
+
+    status = MR_NFS;
+    EXEC SQL DECLARE csr101 CURSOR FOR
+      SELECT nfsphys_id, TRIM (dir) FROM nfsphys
+       WHERE mach_id = :mach_id
+       ORDER BY 2 DESC;
+    if (ingres_errno)
+       return(mr_errcode);
+    EXEC SQL OPEN csr101;
+    if (ingres_errno)
+       return(mr_errcode);
+    while(1) {
+       EXEC SQL FETCH csr101 INTO :_var_phys_id, :dir;
+       if(sqlca.sqlcode != 0) break;
+       cp1 = name;
+       cp2 = dir;
+       while (*cp2) {
+           if (*cp1++ != *cp2) break;
+           cp2++;
+       }
+       if (*cp2 == 0) {
+           status = MR_SUCCESS;
+           break;
+       }
+    }
+    EXEC SQL CLOSE csr101;
+    if (ingres_errno)
+       return(mr_errcode);
+    return(status);
+}
+
+
+/* setup_dfil: free any quota records and fsgroup info associated with
+ * a filesystem when it is deleted.  Also adjust the allocation numbers.
+ */
+
+setup_dfil(q, argv, cl)
+    struct query  *q;
+    char **argv;
+    client *cl;
+{
+    EXEC SQL BEGIN DECLARE SECTION;
+    int id, total, phys_id;
+    short int none;
+    EXEC SQL END DECLARE SECTION;
+
+    id = *(int *)argv[0];
+    EXEC SQL REPEATED SELECT SUM (quota) INTO :total:none FROM quota
+      WHERE filsys_id = :id;
+
+    if(none) total=0;
+
+    /** What if there are multiple phys_id's per f/s? (bad data) **/
+    EXEC SQL REPEATED SELECT phys_id INTO :phys_id FROM filesys
+      WHERE filsys_id = :id;
+    EXEC SQL REPEATED UPDATE nfsphys SET allocated = allocated - :total
+      WHERE nfsphys_id = :phys_id;
+
+    if(!none) {
+       EXEC SQL REPEATED DELETE FROM quota WHERE filsys_id = :id;
+    }
+    EXEC SQL REPEATED DELETE FROM fsgroup WHERE filsys_id = :id;
+    EXEC SQL REPEATED DELETE FROM fsgroup WHERE group_id = :id;
+    if (ingres_errno) return(mr_errcode);
+    return(MR_SUCCESS);
+}
+
+
+/* setup_dnfp: check to see that the nfs physical partition does not have
+ * any filesystems assigned to it before allowing it to be deleted.
+ */
+
+setup_dnfp(q, argv, cl)
+    struct query  *q;
+    char **argv;
+    client *cl;
+{
+    EXEC SQL BEGIN DECLARE SECTION;
+    int id, cnt;
+    char *dir;
+    EXEC SQL END DECLARE SECTION;
+
+    id = *(int *)argv[0];
+    dir = argv[1];
+    EXEC SQL REPEATED SELECT fs.tid INTO :cnt FROM filesys fs, nfsphys np
+      WHERE fs.mach_id = :id AND fs.phys_id = np.nfsphys_id
+       AND np.mach_id = :id AND np.dir = :dir;
+    if (cnt > 0)
+      return(MR_IN_USE);
+    if (ingres_errno)
+      return(mr_errcode);
+    return(MR_SUCCESS);
+}
+
+
+/* setup_dqot: Remove allocation from nfsphys before deleting quota.
+ *   argv[0] = filsys_id
+ *   argv[1] = type if "update_quota" or "delete_quota"
+ *   argv[2 or 1] = users_id or list_id
+ */
+
+setup_dqot(q, argv, cl)
+    struct query  *q;
+    char **argv;
+    client *cl;
+{
+    EXEC SQL BEGIN DECLARE SECTION;
+    int quota, fs, id, physid;
+    char *qtype;
+    EXEC SQL END DECLARE SECTION;
+
+    fs = *(int *)argv[0];
+    if (!strcmp(q->name, "update_quota") || !strcmp(q->name, "delete_quota")) {
+       qtype = argv[1];
+       id = *(int *)argv[2];
+    } else {
+       qtype = "USER";
+       id = *(int *)argv[1];
+    }
+
+    EXEC SQL REPEATED SELECT quota INTO :quota FROM quota
+      WHERE type = :qtype AND entity_id = :id AND filsys_id = :fs;
+    EXEC SQL REPEATED SELECT phys_id INTO :physid FROM filesys 
+      WHERE filsys_id = :fs;
+    EXEC SQL REPEATED UPDATE nfsphys SET allocated = allocated - :quota
+      WHERE nfsphys_id = :physid;
+
+    if (ingres_errno) return(mr_errcode);
+    return(MR_SUCCESS);
+}
+
+
+/* setup_sshi: don't exclusive lock the machine table during
+ * set_server_host_internal.
+ */
+/** Not allowed under (INGRES) SQL **/
+setup_sshi(q, argv, cl)
+    struct query  *q;
+    char **argv;
+    client *cl;
+{
+#if 0
+#ifsql INGRES
+    EXEC SQL set lockmode session where readlock = system;
+#endsql
+#endif
+    return(MR_SUCCESS);
+}
+
+
+/* setup add_kerberos_user_mapping: add the string to the string
+ * table if necessary.
+ */
+
+setup_akum(q, argv, cl)
+struct query *q;
+char **argv;
+client *cl;
+{
+    EXEC SQL BEGIN DECLARE SECTION;
+    int id, rowcount;
+    char *name;
+    EXEC SQL END DECLARE SECTION;
+
+    name = argv[1];
+    if (name_to_id(name, "STRING", &id) != MR_SUCCESS) {
+       if (q->type != APPEND) return(MR_STRING);
+       id=add_string(name);
+       cache_entry(name, "STRING", id);
+    }
+    if (ingres_errno) return(mr_errcode);
+    *(int *)argv[1] = id;
+    return(MR_SUCCESS);
+}
+
diff --git a/server/qvalidate.dc b/server/qvalidate.dc
new file mode 100644 (file)
index 0000000..9022334
--- /dev/null
@@ -0,0 +1,740 @@
+/*
+ *     $Source$
+ *     $Author$
+ *     $Header$
+ *
+ *     Copyright (C) 1987 by the Massachusetts Institute of Technology
+ *     For copying and distribution information, please see the file
+ *     <mit-copyright.h>.
+ *
+ */
+
+#ifndef lint
+static char *rcsid_qsupport_dc = "$Header$";
+#endif lint
+
+#include <mit-copyright.h>
+#include <unistd.h>
+#include "query.h"
+#include "mr_server.h"
+#include <ctype.h>
+EXEC SQL INCLUDE sqlca;
+EXEC SQL INCLUDE sqlda;
+#include "qrtn.h"
+
+extern char *whoami;
+extern int ingres_errno, mr_errcode;
+
+EXEC SQL BEGIN DECLARE SECTION;
+extern char stmt_buf[];
+EXEC SQL END DECLARE SECTION;
+
+EXEC SQL WHENEVER SQLERROR CALL ingerr;
+
+
+/* Validation Routines */
+
+validate_row(q, argv, v)
+    register struct query *q;
+    char *argv[];
+    register struct validate *v;
+{
+    EXEC SQL BEGIN DECLARE SECTION;
+    char *name;
+    char qual[128];
+    int rowcount;
+    EXEC SQL END DECLARE SECTION;
+
+    /* build where clause */
+    build_qual(v->qual, v->argc, argv, qual);
+
+    if (log_flags & LOG_VALID)
+       /* tell the logfile what we're doing */
+       com_err(whoami, 0, "validating row: %s", qual);
+
+    /* look for the record */
+    sprintf(stmt_buf,"SELECT COUNT (*) FROM %s WHERE %s",q->rtable,qual);
+    EXEC SQL PREPARE stmt INTO :SQLDA USING NAMES FROM :stmt_buf;
+    if(sqlca.sqlcode)
+      return(MR_INTERNAL);
+    EXEC SQL DECLARE csr126 CURSOR FOR stmt;
+    EXEC SQL OPEN csr126;
+    EXEC SQL FETCH csr126 USING DESCRIPTOR :SQLDA;
+    EXEC SQL CLOSE csr126;
+    rowcount = *(int *)SQLDA->sqlvar[0].sqldata;
+
+    if (ingres_errno) return(mr_errcode);
+    if (rowcount == 0) return(MR_NO_MATCH);
+    if (rowcount > 1) return(MR_NOT_UNIQUE);
+    return(MR_EXISTS);
+}
+
+validate_fields(q, argv, vo, n)
+    struct query *q;
+    register char *argv[];
+    register struct valobj *vo;
+    register int n;
+{
+    register int status;
+
+    while (--n >= 0) {
+       switch (vo->type) {
+       case V_NAME:
+           if (log_flags & LOG_VALID)
+               com_err(whoami, 0, "validating %s in %s: %s",
+                   vo->namefield, vo->table, argv[vo->index]);
+           status = validate_name(argv, vo);
+           break;
+
+       case V_ID:
+           if (log_flags & LOG_VALID)
+               com_err(whoami, 0, "validating %s in %s: %s",
+                   vo->idfield, vo->table, argv[vo->index]);
+           status = validate_id(q, argv, vo);
+           break;
+
+       case V_DATE:
+           if (log_flags & LOG_VALID)
+               com_err(whoami, 0, "validating date: %s", argv[vo->index]);
+           status = validate_date(argv, vo);
+           break;
+
+       case V_TYPE:
+           if (log_flags & LOG_VALID)
+               com_err(whoami, 0, "validating %s type: %s",
+                   vo->table, argv[vo->index]);
+           status = validate_type(argv, vo);
+           break;
+
+       case V_TYPEDATA:
+           if (log_flags & LOG_VALID)
+               com_err(whoami, 0, "validating typed data (%s): %s",
+                   argv[vo->index - 1], argv[vo->index]);
+           status = validate_typedata(q, argv, vo);
+           break;
+
+       case V_RENAME:
+           if (log_flags & LOG_VALID)
+               com_err(whoami, 0, "validating rename %s in %s",
+                       argv[vo->index], vo->table);
+           status = validate_rename(argv, vo);
+           break;
+
+       case V_CHAR:
+           if (log_flags & LOG_VALID)
+             com_err(whoami, 0, "validating chars: %s", argv[vo->index]);
+           status = validate_chars(argv[vo->index]);
+           break;
+
+       case V_SORT:
+           status = MR_EXISTS;
+           break;
+
+       case V_LOCK:
+           status = lock_table(vo);
+           break;
+
+       case V_WILD:
+           status = convert_wildcards(argv[vo->index]);
+           break;
+
+       case V_UPWILD:
+           status = convert_wildcards_uppercase(argv[vo->index]);
+           break;
+
+       }
+
+       if (status != MR_EXISTS) return(status);
+       vo++;
+    }
+
+    if (ingres_errno) return(mr_errcode);
+    return(MR_SUCCESS);
+}
+
+
+/* validate_chars: verify that there are no illegal characters in
+ * the string.  Legal characters are printing chars other than
+ * ", *, ?, \, [ and ].
+ */
+static int illegalchars[] = {
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^@ - ^O */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^P - ^_ */
+    0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, /* SPACE - / */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* 0 - ? */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* : - O */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, /* P - _ */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ` - o */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* p - ^? */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+};
+
+validate_chars(s)
+register char *s;
+{
+    while (*s)
+      if (illegalchars[*s++])
+       return(MR_BAD_CHAR);
+    return(MR_EXISTS);
+}
+
+
+validate_id(q, argv, vo)
+    struct query *q;
+    char *argv[];
+    register struct valobj *vo;
+{
+    EXEC SQL BEGIN DECLARE SECTION;
+    char *name, *tbl, *namefield, *idfield;
+    int id, rowcount;
+    EXEC SQL END DECLARE SECTION;
+    int status;
+    register char *c;
+
+    name = argv[vo->index];
+    tbl = vo->table;
+    namefield = vo->namefield;
+    idfield = vo->idfield;
+
+    if ((!strcmp(tbl, "users") && !strcmp(namefield, "login")) ||
+       !strcmp(tbl, "machine") ||
+       !strcmp(tbl, "subnet") ||
+       !strcmp(tbl, "filesys") ||
+       !strcmp(tbl, "list") ||
+       !strcmp(tbl, "cluster") ||
+       !strcmp(tbl, "strings")) {
+       if (!strcmp(tbl, "machine") || !strcmp(tbl, "subnet"))
+         for (c = name; *c; c++) if (islower(*c)) *c = toupper(*c);
+       status = name_to_id(name, tbl, &id);
+       if (status == 0) {
+           *(int *)argv[vo->index] = id;
+           return(MR_EXISTS);
+       } else if (status == MR_NO_MATCH && !strcmp(tbl, "strings") &&
+                  (q->type == APPEND || q->type == UPDATE)) {
+           id=add_string(name);
+           cache_entry(name, "STRING", id);
+           *(int *)argv[vo->index] = id;
+           return(MR_EXISTS);
+       } else if (status == MR_NO_MATCH || status == MR_NOT_UNIQUE)
+         return(vo->error);
+       else
+         return(status);
+    }
+
+    if (!strcmp(namefield, "uid")) {
+       sprintf(stmt_buf,"SELECT %s FROM %s WHERE %s = %s",idfield,tbl,namefield,name);
+    } else {
+       sprintf(stmt_buf,"SELECT %s FROM %s WHERE %s = '%s'",idfield,tbl,namefield,name);
+    }
+    EXEC SQL PREPARE stmt INTO :SQLDA USING NAMES FROM :stmt_buf;
+    if(sqlca.sqlcode)
+      return(MR_INTERNAL);
+    EXEC SQL DECLARE csr127 CURSOR FOR stmt;
+    EXEC SQL OPEN csr127;
+    rowcount=0;
+    EXEC SQL FETCH csr127 USING DESCRIPTOR :SQLDA;
+    if(sqlca.sqlcode == 0) {
+       rowcount++;
+       EXEC SQL FETCH csr127 USING DESCRIPTOR :SQLDA;
+       if(sqlca.sqlcode == 0) rowcount++;
+    }
+    EXEC SQL CLOSE csr127;
+    if (ingres_errno)
+      return(mr_errcode);
+
+    if (rowcount != 1) return(vo->error);
+    bcopy(SQLDA->sqlvar[0].sqldata,argv[vo->index],sizeof(int));
+    return(MR_EXISTS);
+}
+
+validate_name(argv, vo)
+    char *argv[];
+    register struct valobj *vo;
+{
+    EXEC SQL BEGIN DECLARE SECTION;
+    char *name, *tbl, *namefield;
+    int rowcount;
+    EXEC SQL END DECLARE SECTION;
+    register char *c;
+
+    name = argv[vo->index];
+    tbl = vo->table;
+    namefield = vo->namefield;
+    if (!strcmp(tbl, "servers") && !strcmp(namefield, "name")) {
+       for (c = name; *c; c++)
+         if (islower(*c))
+           *c = toupper(*c);
+    }
+    sprintf(stmt_buf,"SELECT DISTINCT COUNT(*) FROM %s WHERE %s.%s = '%s'",
+           tbl,tbl,namefield,name);
+    EXEC SQL PREPARE stmt INTO :SQLDA USING NAMES FROM :stmt_buf;
+    if(sqlca.sqlcode)
+      return(MR_INTERNAL);
+    EXEC SQL DECLARE csr128 CURSOR FOR stmt;
+    EXEC SQL OPEN csr128;
+    EXEC SQL FETCH csr128 USING DESCRIPTOR :SQLDA;
+    rowcount = *(int *)SQLDA->sqlvar[0].sqldata;
+    EXEC SQL CLOSE csr128;
+
+    if (ingres_errno) return(mr_errcode);
+    return ((rowcount == 1) ? MR_EXISTS : vo->error);
+}
+
+validate_date(argv, vo)
+    char *argv[];
+    struct valobj *vo;
+{
+    EXEC SQL BEGIN DECLARE SECTION;
+    char *idate;
+    double dd;
+    int errorno;
+    EXEC SQL END DECLARE SECTION;
+
+    idate = argv[vo->index];
+    EXEC SQL SELECT interval('years',date(:idate)-date('today')) INTO :dd;
+
+    if (sqlca.sqlcode != 0 || dd > 5.0) return(MR_DATE);
+    return(MR_EXISTS);
+}
+
+
+validate_rename(argv, vo)
+char *argv[];
+struct valobj *vo;
+{
+    EXEC SQL BEGIN DECLARE SECTION;
+    char *name, *tbl, *namefield, *idfield;
+    int id;
+    EXEC SQL END DECLARE SECTION;
+    int status;
+    register char *c;
+
+    c = name = argv[vo->index];
+    while (*c)
+      if (illegalchars[*c++])
+       return(MR_BAD_CHAR);
+    tbl = vo->table;
+    /* minor kludge to upcasify machine names */
+    if (!strcmp(tbl, "machine"))
+       for (c = name; *c; c++) if (islower(*c)) *c = toupper(*c);
+    namefield = vo->namefield;
+    idfield = vo->idfield;
+    id = -1;
+    if (idfield == 0) {
+       if (!strcmp(argv[vo->index], argv[vo->index - 1]))
+         return(MR_EXISTS);
+       sprintf(stmt_buf,"SELECT %s FROM %s WHERE %s = LEFT('%s',SIZE(%s))",
+               namefield,tbl,namefield,name,namefield);
+       EXEC SQL PREPARE stmt INTO :SQLDA USING NAMES FROM :stmt_buf;
+       if(sqlca.sqlcode)
+         return(MR_INTERNAL);
+        EXEC SQL DECLARE csr129 CURSOR FOR stmt;
+       EXEC SQL OPEN csr129;
+        EXEC SQL FETCH csr129 USING DESCRIPTOR :SQLDA;
+       if(sqlca.sqlcode == 0) id=1; else id=0;
+       EXEC SQL CLOSE csr129;
+
+       if (ingres_errno) return(mr_errcode);
+       if (id)
+         return(vo->error);
+       else
+         return(MR_EXISTS);
+    }
+    status = name_to_id(name, tbl, &id);
+    if (status == MR_NO_MATCH || id == *(int *)argv[vo->index - 1])
+      return(MR_EXISTS);
+    else
+      return(vo->error);
+}
+
+
+validate_type(argv, vo)
+    char *argv[];
+    register struct valobj *vo;
+{
+    EXEC SQL BEGIN DECLARE SECTION;
+    char *typename;
+    char *val;
+    int cnt;
+    EXEC SQL END DECLARE SECTION;
+    register char *c;
+
+    typename = vo->table;
+    c = val = argv[vo->index];
+    while (*c) {
+       if (illegalchars[*c++])
+         return(MR_BAD_CHAR);
+    }
+
+    /* uppercase type fields */
+    for (c = val; *c; c++) if (islower(*c)) *c = toupper(*c);
+
+    EXEC SQL SELECT COUNT(trans) INTO :cnt FROM alias
+      WHERE name = :typename AND type='TYPE' AND trans = :val;
+    if (ingres_errno) return(mr_errcode);
+    return (cnt ? MR_EXISTS : vo->error);
+}
+
+/* validate member or type-specific data field */
+
+validate_typedata(q, argv, vo)
+    register struct query *q;
+    register char *argv[];
+    register struct valobj *vo;
+{
+    EXEC SQL BEGIN DECLARE SECTION;
+    char *name;
+    char *field_type;
+    char data_type[129];
+    int id;
+    EXEC SQL END DECLARE SECTION;
+    int status;
+    char *index();
+    register char *c;
+
+    /* get named object */
+    name = argv[vo->index];
+
+    /* get field type string (known to be at index-1) */
+    field_type = argv[vo->index-1];
+
+    /* get corresponding data type associated with field type name */
+    EXEC SQL SELECT trans INTO :data_type FROM alias
+      WHERE name = :field_type AND type='TYPEDATA';
+    if (ingres_errno) return(mr_errcode);
+    if (sqlca.sqlerrd[2] != 1) return(MR_TYPE);
+
+    /* now retrieve the record id corresponding to the named object */
+    if (index(data_type, ' '))
+       *index(data_type, ' ') = 0;
+    if (!strcmp(data_type, "user")) {
+       /* USER */
+       if (index(name, '@'))
+         return(MR_USER);
+       status = name_to_id(name, data_type, &id);
+       if (status && (status == MR_NO_MATCH || status == MR_NOT_UNIQUE))
+         return(MR_USER);
+       if (status) return(status);
+    } else if (!strcmp(data_type, "list")) {
+       /* LIST */
+       status = name_to_id(name, data_type, &id);
+       if (status && status == MR_NOT_UNIQUE)
+         return(MR_LIST);
+       if (status == MR_NO_MATCH) {
+           /* if idfield is non-zero, then if argv[0] matches the string
+            * that we're trying to resolve, we should get the value of
+            * numvalues.[idfield] for the id.
+            */
+           if (vo->idfield && !strcmp(argv[0], argv[vo->index])) {
+               set_next_object_id(q->validate->object_id, q->rtable, 0);
+               name = vo->idfield;
+               EXEC SQL REPEATED SELECT value INTO :id FROM numvalues
+                 WHERE name = :name;
+               if (sqlca.sqlerrd[2] != 1) return(MR_LIST);
+           } else
+             return(MR_LIST);
+       } else if (status) return(status);
+    } else if (!strcmp(data_type, "machine")) {
+       /* MACHINE */
+       for (c = name; *c; c++) if (islower(*c)) *c = toupper(*c);
+       status = name_to_id(name, data_type, &id);
+       if (status && (status == MR_NO_MATCH || status == MR_NOT_UNIQUE))
+         return(MR_MACHINE);
+       if (status) return(status);
+    } else if (!strcmp(data_type, "string")) {
+       /* STRING */
+       status = name_to_id(name, data_type, &id);
+       if (status && status == MR_NOT_UNIQUE)
+         return(MR_STRING);
+       if (status == MR_NO_MATCH) {
+           if (q->type != APPEND && q->type != UPDATE) return(MR_STRING);
+           id=add_string(name);
+           cache_entry(name, "STRING", id);
+       } else if (status) return(status);
+    } else if (!strcmp(data_type, "none")) {
+       id = 0;
+    } else {
+       return(MR_TYPE);
+    }
+
+    /* now set value in argv */
+    *(int *)argv[vo->index] = id;
+
+    return (MR_EXISTS);
+}
+
+
+/* Lock the table named by the validation object */
+
+lock_table(vo)
+struct valobj *vo;
+{
+    sprintf(stmt_buf,"UPDATE %s SET modtime='now' WHERE %s.%s = 0",
+           vo->table,vo->table,vo->idfield);
+    EXEC SQL EXECUTE IMMEDIATE :stmt_buf;
+    if (ingres_errno) return(mr_errcode);
+    if (sqlca.sqlerrd[2] != 1)
+      return(vo->error);
+    else
+      return(MR_EXISTS);
+}
+
+
+/* Check the database at startup time.  For now this just resets the
+ * inprogress flags that the DCM uses.
+ */
+
+sanity_check_database()
+{
+}
+
+
+/* Dynamic SQL support routines */
+MR_SQLDA_T *mr_alloc_SQLDA()
+{
+    MR_SQLDA_T *it;
+    short *null_indicators;
+    register int j;
+
+    if((it=(MR_SQLDA_T *)malloc(sizeof(MR_SQLDA_T)))==NULL) {
+       com_err(whoami, MR_NO_MEM, "setting up SQLDA");
+       exit(1);
+    }
+
+    if((null_indicators=(short *)calloc(QMAXARGS,sizeof(short)))==NULL) {
+       com_err(whoami, MR_NO_MEM, "setting up SQLDA null indicators");
+       exit(1);
+    }
+
+    for(j=0; j<QMAXARGS; j++) {
+       if((it->sqlvar[j].sqldata=malloc(sizeof(short)+ARGLEN))==NULL) {
+           com_err(whoami, MR_NO_MEM, "setting up SQLDA variables");
+           exit(1);
+       }
+       it->sqlvar[j].sqllen=ARGLEN;
+       it->sqlvar[j].sqlind=null_indicators+j;
+       null_indicators[j]=0;
+    }
+    it->sqln=QMAXARGS;
+    return it;
+}
+
+
+/* Use this after FETCH USING DESCRIPTOR one or more
+ * result columns may contain NULLs.  This routine is
+ * not currently needed, since db/schema creates all
+ * columns with a NOT NULL WITH DEFAULT clause.
+ *
+ * This is currently dead flesh, since no Moira columns
+ * allow null values; all use default values.
+ */
+mr_fix_nulls_in_SQLDA(da)
+    MR_SQLDA_T *da;
+{
+    register IISQLVAR *var;
+    register int j;
+    int *intp;
+
+    for(j=0, var=da->sqlvar; j<da->sqld; j++, var++) {
+       switch(var->sqltype) {
+         case -IISQ_CHA_TYPE:
+           if(*var->sqlind)
+             *var->sqldata='\0';
+           break;
+         case -IISQ_INT_TYPE:
+           if(*var->sqlind) {
+               intp=(int *)var->sqldata;
+               *intp=0;
+           }
+           break;
+       }
+    }
+}
+
+/* prefetch_value():
+ * This routine fetches an appropriate value from the numvalues table.
+ * It is a little hack to get around the fact that SQL doesn't let you
+ * do something like INSERT INTO table (foo) VALUES (other_table.bar).
+ *
+ * It is called from the query table as (*v->pre_rtn)(q,Argv,cl) or
+ * from within a setup_...() routine with the appropriate arguments.
+ *
+ * Correct functioning of this routine may depend on the assumption
+ * that this query is an APPEND.
+ */
+
+prefetch_value(q,argv,cl)
+    struct query *q;
+    char **argv;
+    client *cl;
+{
+    EXEC SQL BEGIN DECLARE SECTION;
+    char *name = q->validate->object_id;
+    int value;
+    EXEC SQL END DECLARE SECTION;
+    int status, limit, argc;
+
+    /* set next object id, limiting it if necessary */
+    if(!strcmp(name, "uid") || !strcmp(name, "gid"))
+      limit = 1; /* So far as I know, this isn't needed.  Just CMA. */
+    else
+      limit = 0;
+    if((status = set_next_object_id(name, q->rtable, limit)) != MR_SUCCESS)
+      return(status);
+
+    /* fetch object id */
+    EXEC SQL SELECT value INTO :value FROM numvalues WHERE name=:name;
+    if(ingres_errno) return(mr_errcode);
+    if(sqlca.sqlerrd[2] != 1) return(MR_INTERNAL);
+
+    argc = q->argc + q->vcnt;   /* end of Argv for APPENDs */
+    sprintf(argv[argc],"%d",value);  /** Could save this step by changing tlist from %s to %d **/
+
+    return(MR_SUCCESS);
+}
+
+/* prefetch_filesys():
+ * Fetches the phys_id from filesys based on the filsys_id in argv[0].
+ * Appends the filsys_id and the phys_id to the argv so they can be
+ * referenced in an INSERT into a table other than filesys.  Also
+ * see comments at prefetch_value().
+ *
+ * Assumes the existence of a row where filsys_id = argv[0], since a
+ * filesys label has already been resolved to a filsys_id.
+ */
+prefetch_filesys(q,argv,cl)
+    struct query *q;
+    char **argv;
+    client *cl;
+{
+    EXEC SQL BEGIN DECLARE SECTION;
+    int fid,phid;
+    EXEC SQL END DECLARE SECTION;
+    int argc;
+
+    fid = *(int *)argv[0];
+    EXEC SQL SELECT phys_id INTO :phid FROM filesys WHERE filsys_id = :fid;
+    if(ingres_errno) return(mr_errcode);
+
+    argc=q->argc+q->vcnt;
+    sprintf(argv[argc++],"%d",phid);
+    sprintf(argv[argc],"%d",fid);
+
+    return(MR_SUCCESS);
+}
+
+/* Convert normal Unix-style wildcards to SQL voodoo */
+convert_wildcards(arg)
+    char *arg;
+{
+    static char buffer[ARGLEN];
+    register char *s, *d;
+
+    for(d=buffer,s=arg;*s;s++) {
+       switch(*s) {
+         case '*': *d++='%'; *d++='%'; break;
+         case '?': *d++='_'; break;
+         case '_':
+         case '[':
+         case ']': *d++='*'; *d++ = *s; break;
+         case '%': *d++='*'; *d++='%'; *d++='%'; break;
+         default: *d++ = *s; break;
+       }
+    }
+    *d='\0';
+
+    /* Copy back into argv */
+    strcpy(arg,buffer);
+
+    return(MR_EXISTS);
+}
+
+/* This version includes uppercase conversion, for things like gmac.
+ * This is necessary because "LIKE" doesn't work with "uppercase()".
+ * Including it in a wildcard routine saves making two passes over
+ * the argument string.
+ */
+convert_wildcards_uppercase(arg)
+    char *arg;
+{
+    static char buffer[ARGLEN];
+    register char *s, *d;
+
+    for(d=buffer,s=arg;*s;s++) {
+       switch(*s) {
+         case '*': *d++='%'; *d++='%'; break;
+         case '?': *d++='_'; break;
+         case '_':
+         case '[':
+         case ']': *d++='*'; *d++ = *s; break;
+         case '%': *d++='*'; *d++='%'; *d++='%'; break;
+         default: *d++=toupper(*s); break;       /* This is the only diff. */
+       }
+    }
+    *d='\0';
+
+    /* Copy back into argv */
+    strcpy(arg,buffer);
+
+    return(MR_EXISTS);
+}
+
+
+/* Looks like it's time to build an abstraction barrier, Yogi */
+mr_select_any(stmt)
+    EXEC SQL BEGIN DECLARE SECTION; 
+    char *stmt;
+    EXEC SQL END DECLARE SECTION; 
+{
+    int result=0;
+
+    EXEC SQL PREPARE stmt FROM :stmt;
+    EXEC SQL DESCRIBE stmt INTO :SQLDA;
+    if(SQLDA->sqld==0)                       /* Not a SELECT */
+        return(MR_INTERNAL);        
+    EXEC SQL DECLARE csr CURSOR FOR stmt;
+    EXEC SQL OPEN csr;
+    EXEC SQL FETCH csr USING DESCRIPTOR :SQLDA;
+    if(sqlca.sqlcode==0) 
+        result=MR_EXISTS;
+    else if((sqlca.sqlcode<0) && mr_errcode)
+        result=mr_errcode;
+    else
+        result=0;
+    EXEC SQL CLOSE csr;
+    return(result);
+} 
+
+
+
+/*  Adds a string to the string table.  Returns the id number.
+ * 
+ */
+int add_string(name)
+    EXEC SQL BEGIN DECLARE SECTION; 
+    char *name;
+    EXEC SQL END DECLARE SECTION;
+{
+    EXEC SQL BEGIN DECLARE SECTION;
+    char buf[256];
+    int id;
+    EXEC SQL END DECLARE SECTION; 
+
+    EXEC SQL SELECT value INTO :id FROM numvalues WHERE name = 'strings_id';
+    id++;
+    EXEC SQL UPDATE numvalues SET value = :id WHERE name = 'strings_id';
+    
+    /* Use sprintf to get around problem with doubled single quotes */
+    sprintf(buf,"INSERT INTO strings (string_id, string) VALUES (%d, '%s')",id,name);
+    EXEC SQL EXECUTE IMMEDIATE :buf;
+    return(id);
+}
+
This page took 0.145589 seconds and 5 git commands to generate.