From: mar Date: Mon, 1 Nov 1993 12:04:03 +0000 (+0000) Subject: Initial revision X-Git-Tag: release77~86 X-Git-Url: http://andersk.mit.edu/gitweb/moira.git/commitdiff_plain/73cf66ba53ada0c6284f93e9d52ab9095fd7d784 Initial revision --- diff --git a/server/qaccess.dc b/server/qaccess.dc new file mode 100644 index 00000000..234e5607 --- /dev/null +++ b/server/qaccess.dc @@ -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 + * . + * + */ + +#ifndef lint +static char *rcsid_qsupport_dc = "$Header$"; +#endif lint + +#include +#include "query.h" +#include "mr_server.h" +#include +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 index 00000000..fc13e1db --- /dev/null +++ b/server/qfollow.dc @@ -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 + * . + * + */ + +#ifndef lint +static char *rcsid_qsupport_dc = "$Header$"; +#endif lint + +#include +#include "query.h" +#include "mr_server.h" +#include +#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 index 00000000..ff54b7c3 --- /dev/null +++ b/server/qsetup.dc @@ -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 + * . + * + */ + +#ifndef lint +static char *rcsid_qsupport_dc = "$Header$"; +#endif lint + +#include +#include "query.h" +#include "mr_server.h" +#include +#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] = "#" + */ + +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 index 00000000..90223341 --- /dev/null +++ b/server/qvalidate.dc @@ -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 + * . + * + */ + +#ifndef lint +static char *rcsid_qsupport_dc = "$Header$"; +#endif lint + +#include +#include +#include "query.h" +#include "mr_server.h" +#include +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; jsqlvar[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; jsqld; 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); +} +