5 * Copyright (C) 1987-1998 by the Massachusetts Institute of Technology
6 * For copying and distribution information, please see the file
10 #include <mit-copyright.h>
11 #include "mr_server.h"
15 #include <arpa/inet.h>
16 #include <netinet/in.h>
22 EXEC SQL INCLUDE sqlca;
27 extern int dbms_errno, mr_errcode;
29 EXEC SQL BEGIN DECLARE SECTION;
30 extern char stmt_buf[];
31 EXEC SQL END DECLARE SECTION;
33 EXEC SQL WHENEVER SQLERROR DO dbmserr();
35 int hostname_check(char *name);
36 int hostinfo_check(char *name, int num);
37 int prefetch_value(struct query *q, char **argv, client *cl);
38 int check_nfs(int mach_idx, char *name, char *access);
39 int check_mailman_listname(char *name, const char *suffix);
43 /* Setup routine for add_user
45 * Inputs: argv[0] - login
50 * - if argv[1] == UNIQUE_UID then set argv[1] = next(uid)
51 * - if argv[0] == UNIQUE_LOGIN then set argv[0] = "#<uid>"
54 int setup_ausr(struct query *q, char *argv[], client *cl)
57 EXEC SQL BEGIN DECLARE SECTION;
59 EXEC SQL END DECLARE SECTION;
61 if (!strcmp(q->shortname, "uusr") || !strcmp(q->shortname, "uuac"))
68 if (strlen(argv[row + 3]) + strlen(argv[row + 4]) +
69 strlen(argv[row + 5]) + 2 > USERS_FULLNAME_SIZE)
70 return MR_ARG_TOO_LONG;
74 if (strlen(argv[row + 2]) + strlen(argv[row + 3]) +
75 strlen(argv[row + 4]) + 2 > USERS_FULLNAME_SIZE)
76 return MR_ARG_TOO_LONG;
79 if (!strcmp(argv[row], UNIQUE_UID) || atoi(argv[row]) == -1)
81 if ((err = set_next_object_id("unix_uid", USERS_TABLE, 1)))
83 EXEC SQL SELECT value INTO :nuid FROM numvalues WHERE name = 'unix_uid';
84 if (sqlca.sqlerrd[2] != 1)
86 sprintf(argv[row], "%d", nuid);
89 if (!strcmp(argv[0], UNIQUE_LOGIN) || atoi(argv[row]) == -1)
90 sprintf(argv[0], "#%s", argv[row]);
92 if ((mr_errcode = prefetch_value(q, argv, cl)) != MR_SUCCESS)
95 /* If this is an UPDATE query, we're done. */
99 /* For an add query, we need to fill in the creator id. */
100 sprintf(argv[q->argc + q->vcnt + 1], "%d", cl->client_id);
105 /* setup_dusr - verify that the user is no longer being referenced
106 * and may safely be deleted.
109 int setup_dusr(struct query *q, char *argv[], client *cl)
111 EXEC SQL BEGIN DECLARE SECTION;
113 char resv[USERS_RESERVATIONS_SIZE];
114 EXEC SQL END DECLARE SECTION;
116 id = *(int *)argv[0];
118 /* For now, only allow users to be deleted if their status is
119 * one of 0, 4, or 8 (the various registerable statuses)
120 * and we have no reservations about deleting them.
122 EXEC SQL SELECT status, reservations INTO :flag, :resv
123 FROM users WHERE users_id = :id;
124 if ((flag != 0 && flag != 4 && flag != 8) || *resv)
127 EXEC SQL SELECT COUNT(member_id) INTO :cnt FROM imembers
128 WHERE member_id = :id AND member_type = 'USER';
131 EXEC SQL SELECT COUNT(label) INTO :cnt FROM filesys
135 EXEC SQL SELECT COUNT(name) INTO :cnt FROM list
136 WHERE acl_id = :id AND acl_type = 'USER';
139 EXEC SQL SELECT COUNT(name) INTO :cnt FROM servers
140 WHERE acl_id = :id AND acl_type = 'USER';
143 EXEC SQL SELECT COUNT(acl_id) INTO :cnt FROM hostaccess
144 WHERE acl_id = :id AND acl_type = 'USER';
150 EXEC SQL DELETE FROM quota WHERE entity_id = :id AND type = 'USER';
151 EXEC SQL DELETE FROM krbmap WHERE users_id = :id;
156 /* setup_dpob: Take care of keeping track of the post office usage.
158 int setup_dpob(struct query *q, char *argv[], client *cl)
160 EXEC SQL BEGIN DECLARE SECTION;
162 char type[USERS_POTYPE_SIZE];
163 EXEC SQL END DECLARE SECTION;
165 user = *(int *)argv[0];
166 EXEC SQL SELECT potype, pop_id INTO :type, :id FROM users
167 WHERE users_id = :user;
171 if (!strcmp(strtrim(type), "POP"))
172 set_pop_usage(id, -1);
177 /* setup_dmac - verify that the machine is no longer being referenced
178 * and may safely be deleted.
181 int setup_dmac(struct query *q, char *argv[], client *cl)
183 EXEC SQL BEGIN DECLARE SECTION;
185 EXEC SQL END DECLARE SECTION;
187 id = *(int *)argv[0];
189 EXEC SQL SELECT status INTO :flag FROM machine
193 EXEC SQL SELECT COUNT(login) INTO :cnt FROM users
194 WHERE potype = 'POP' AND pop_id = :id;
197 EXEC SQL SELECT COUNT(mach_id) INTO :cnt FROM serverhosts
201 EXEC SQL SELECT COUNT(mach_id) INTO :cnt FROM nfsphys
205 EXEC SQL SELECT COUNT(mach_id) INTO :cnt FROM hostaccess
209 EXEC SQL SELECT COUNT(mach_id) INTO :cnt FROM printers
213 EXEC SQL SELECT COUNT(rm) INTO :cnt FROM printers
217 EXEC SQL SELECT COUNT(rq) INTO :cnt FROM printers
221 EXEC SQL SELECT COUNT(mach_id) INTO :cnt FROM printservers
225 EXEC SQL SELECT COUNT(mach_id) INTO :cnt FROM hostalias
229 EXEC SQL SELECT COUNT(member_id) INTO :cnt FROM imembers
230 WHERE member_type = 'MACHINE' and member_id = :id;
234 EXEC SQL DELETE FROM mcmap WHERE mach_id = :id;
238 EXEC SQL DELETE FROM mcntmap WHERE mach_id = :id;
244 /* setup_asnt - verify that the data entered for the subnet is sane.
245 * In particular, make sure that the "low" and "high" addresses are
246 * correctly ordered, i.e., high > low.
249 int setup_asnt(struct query *q, char *argv[], client *cl)
251 int high, low, row, status;
252 char *account_number;
254 /* Check for asnt or usnt. */
255 if (q->type == APPEND)
260 low = atoi(argv[row + 7]);
261 high = atoi(argv[row + 8]);
262 status = atoi(argv[row + 2]);
263 account_number = argv[row + 4];
265 /* Don't allow Private subnets to be created without a valid billing
268 if (status == SNET_STATUS_PRIVATE_10MBPS ||
269 status == SNET_STATUS_PRIVATE_100MBPS ||
270 status == SNET_STATUS_PRIVATE_1000MBPS)
272 EXEC SQL SELECT account_number FROM accountnumbers
273 WHERE account_number = :account_number;
274 if (sqlca.sqlcode == SQL_NO_MATCH)
275 return MR_ACCOUNT_NUMBER;
278 /* Special case 0.0.0.0 and 255.255.255.255 */
279 if (!(low == 0 || low == -1 || high == 0 || high == -1))
283 /* If this is update_subnet, we're done. */
287 /* For an add_subnet query, allocate and fill in a new snet_id */
288 if ((mr_errcode = prefetch_value(q, argv, cl)) != MR_SUCCESS)
294 /* setup_dsnt - verify that the subnet is no longer being referenced
295 * and may safely be deleted.
298 int setup_dsnt(struct query *q, char *argv[], client *cl)
300 EXEC SQL BEGIN DECLARE SECTION;
302 EXEC SQL END DECLARE SECTION;
304 id = *(int *)argv[0];
305 EXEC SQL SELECT COUNT(mach_id) INTO :cnt FROM machine
313 /* setup_dclu - verify that the cluster is no longer being referenced
314 * and may safely be deleted.
317 int setup_dclu(struct query *q, char *argv[], client *cl)
319 EXEC SQL BEGIN DECLARE SECTION;
321 EXEC SQL END DECLARE SECTION;
323 id = *(int *)argv[0];
324 EXEC SQL SELECT COUNT(mach_id) INTO :cnt FROM mcmap
328 EXEC SQL SELECT COUNT(clu_id) INTO :cnt FROM svc
338 /* setup_alis - if argv[5] is non-zero and argv[6] is UNIQUE_ID, then allocate
339 * a new gid and put it in argv[6]. Otherwise if argv[6] is UNIQUE_ID but
340 * argv[5] is not, then remember that UNIQUE_ID is being stored by putting
341 * a -1 there. Remember that this is also used for ulis, with the indexes
342 * at 6 & 7. Also check that the list name does not contain uppercase
343 * characters, control characters, @, or :.
345 * Newlines in list descriptions do bad things to the aliases file
346 * moira generates, so make sure the description doesn't contain any, too.
349 static int badlistchars[] = {
350 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^@ - ^O */
351 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^P - ^_ */
352 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, /* SPACE - / */
353 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, /* 0 - ? */
354 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* @ - O */
355 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, /* P - _ */
356 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ` - o */
357 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, /* p - ^? */
358 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
359 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
360 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
361 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
362 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
363 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
364 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
365 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
368 int setup_alis(struct query *q, char *argv[], client *cl)
370 EXEC SQL BEGIN DECLARE SECTION;
371 int ngid, cnt, mailman, mailman_id;
373 EXEC SQL END DECLARE SECTION;
375 int idx, err, best = -1, usage;
377 if (!strcmp(q->shortname, "alis"))
379 else if (!strcmp(q->shortname, "ulis"))
384 desc = argv[9 + idx];
385 else if (q->version == 3)
386 desc = argv[10 + idx];
387 else if (q->version == 4)
388 desc = argv[12 + idx];
389 else if (q->version >= 10)
390 desc = argv[14 + idx];
394 EXEC SQL BEGIN DECLARE SECTION;
395 int lid = *(int *)argv[0];
396 EXEC SQL END DECLARE SECTION;
398 if (acl_access_check(lid, cl))
402 for (p = (unsigned char *) name; *p; p++)
404 if (badlistchars[*p])
408 for (p = (unsigned char *) desc; *p; p++)
414 /* Check that it doesn't conflict with a pre-existing weirdly-cased
416 EXEC SQL SELECT COUNT(name) INTO :cnt FROM list
417 WHERE LOWER(name) = :name AND name != :name;
421 if (!strcmp(argv[6 + idx], UNIQUE_GID) || atoi(argv[6 + idx]) == -1)
423 if (atoi(argv[5 + idx]))
425 if ((err = set_next_object_id("gid", LIST_TABLE, 1)))
427 EXEC SQL SELECT value INTO :ngid FROM numvalues
431 sprintf(argv[6 + idx], "%d", ngid);
434 strcpy(argv[6 + idx], "-1");
437 /* Don't let someone rename a list to one of the magic mailman names
438 * (foo-admin, etc) if foo already exists as a mailman list.
440 if ((err = check_mailman_listname(name, "-admin")) != MR_SUCCESS)
442 if ((err = check_mailman_listname(name, "-owner")) != MR_SUCCESS)
444 if ((err = check_mailman_listname(name, "-request")) != MR_SUCCESS)
447 if (q->version >= 10)
449 /* Don't let them take this name for a mailman list if we can't
450 * reserve the -admin, -owner, and -request names.
452 if (atoi(argv[8 + idx]))
454 EXEC SQL SELECT COUNT(name) INTO :cnt FROM list
455 WHERE (name = :name || '-admin' OR name = :name || '-owner' OR
456 name = :name || '-request');
461 /* Handle the [ANY] case for mailman server. */
462 mailman_id = *(int *)argv[9 + idx];
463 if (mailman_id == -1)
465 EXEC SQL DECLARE csr_mailman CURSOR FOR
466 SELECT mach_id FROM serverhosts WHERE service = 'MAILMAN'
470 EXEC SQL OPEN csr_mailman;
476 EXEC SQL FETCH csr_mailman INTO :mailman_id;
480 EXEC SQL SELECT COUNT(name) INTO :usage FROM list
481 WHERE mailman_id = :mailman_id;
483 if (best < 0 || usage < best)
486 *(int *)argv[9 + idx] = mailman_id;
490 EXEC SQL CLOSE csr_mailman;
500 /* Client too old to know about the mailman code.
501 * Use existing value of mailman boolean in the table.
503 EXEC SQL SELECT mailman INTO :mailman FROM list WHERE name = :name;
506 EXEC SQL SELECT COUNT(name) INTO :cnt FROM list
507 WHERE (name = :name || '-admin' OR name = :name || '-owner' OR
508 name = :name || '-request');
514 if ((mr_errcode = prefetch_value(q, argv, cl)) != MR_SUCCESS)
521 /* setup_dlis - verify that the list is no longer being referenced
522 * and may safely be deleted.
525 int setup_dlis(struct query *q, char *argv[], client *cl)
528 EXEC SQL BEGIN DECLARE SECTION;
530 EXEC SQL END DECLARE SECTION;
532 id = *(int *)argv[0];
534 EXEC SQL SELECT COUNT(member_id) INTO :cnt FROM imembers
535 WHERE member_id = :id AND member_type = 'LIST';
539 EXEC SQL SELECT COUNT(member_id) INTO :cnt FROM imembers
540 WHERE member_id = :id AND member_type = 'LIST';
544 EXEC SQL SELECT COUNT(member_id) INTO :cnt FROM imembers
549 EXEC SQL SELECT COUNT(label) INTO :cnt FROM filesys WHERE owners = :id;
553 EXEC SQL SELECT COUNT(tag) INTO :cnt FROM capacls WHERE list_id = :id;
557 EXEC SQL SELECT COUNT(name) INTO :cnt FROM list
558 WHERE acl_id = :id AND acl_type = 'LIST' AND list_id != :id;
562 EXEC SQL SELECT COUNT(name) INTO :cnt FROM list
563 WHERE memacl_id = :id AND memacl_type = 'LIST' AND list_id != :id;
567 EXEC SQL SELECT COUNT(name) INTO :cnt FROM servers
568 WHERE acl_id = :id AND acl_type = 'LIST';
572 EXEC SQL SELECT COUNT(entity_id) INTO :cnt FROM quota
573 WHERE entity_id = :id AND type = 'GROUP';
577 EXEC SQL SELECT COUNT(acl_id) INTO :cnt FROM hostaccess
578 WHERE acl_id = :id AND acl_type = 'LIST';
582 EXEC SQL SELECT COUNT(class) INTO :cnt FROM zephyr z
583 WHERE z.xmt_type = 'LIST' AND z.xmt_id = :id
584 OR z.sub_type = 'LIST' AND z.sub_id = :id
585 OR z.iws_type = 'LIST' AND z.iws_id = :id
586 OR z.iui_type = 'LIST' AND z.iui_id = :id
587 OR z.owner_type = 'LIST' and z.owner_id = :id;
591 EXEC SQL SELECT COUNT(name) INTO :cnt FROM printers
592 WHERE lpc_acl = :id OR ac = :id;
596 EXEC SQL SELECT COUNT(mach_id) INTO :cnt FROM printservers
597 WHERE owner_type = 'LIST' AND owner_id = :id
602 EXEC SQL SELECT count(name) INTO :cnt FROM containers
603 WHERE acl_id = :id AND acl_type = 'LIST';
607 EXEC SQL SELECT count(name) INTO :cnt FROM containers
608 WHERE memacl_id = :id AND memacl_type = 'LIST';
616 /* setup_dsin - verify that the service is no longer being referenced
617 * and may safely be deleted.
620 int setup_dsin(struct query *q, char *argv[], client *cl)
622 EXEC SQL BEGIN DECLARE SECTION;
625 EXEC SQL END DECLARE SECTION;
628 EXEC SQL SELECT COUNT(service) INTO :cnt FROM serverhosts
629 WHERE service = UPPER(:svrname);
633 EXEC SQL SELECT inprogress INTO :ec FROM servers
634 WHERE name = UPPER(:svrname);
644 /* setup_dshi - verify that the service-host is no longer being referenced
645 * and may safely be deleted.
648 int setup_dshi(struct query *q, char *argv[], client *cl)
650 EXEC SQL BEGIN DECLARE SECTION;
653 EXEC SQL END DECLARE SECTION;
656 id = *(int *)argv[1];
658 EXEC SQL SELECT inprogress INTO :ec FROM serverhosts
659 WHERE service = UPPER(:svrname) AND mach_id = :id;
670 ** setup_add_filesys - verify existance of referenced file systems
676 ** argv[5] - rwaccess
681 ** - for type = NFS/IMAP:
682 ** * extract directory prefix from name
683 ** * verify mach_id/dir in nfsphys
684 ** * verify rwaccess in {r, w, R, W}
686 ** Side effect: sets variable _var_phys_id to the ID of the physical
687 ** filesystem (nfsphys_id for NFS, 0 for RVD)
690 ** MR_NFS - specified directory not exported
691 ** MR_FILESYS_ACCESS - invalid filesys access
695 EXEC SQL BEGIN DECLARE SECTION;
697 EXEC SQL END DECLARE SECTION;
699 int setup_afil(struct query *q, char *argv[], client *cl)
703 EXEC SQL BEGIN DECLARE SECTION;
705 char ftype[FILESYS_TYPE_SIZE + 10], *rwaccess;
706 EXEC SQL END DECLARE SECTION;
709 mach_id = *(int *)argv[2];
714 sprintf(ftype, "fs_access_%s", type);
715 EXEC SQL SELECT COUNT(trans) INTO :ok FROM alias
716 WHERE name = :ftype AND type = 'TYPE' and trans = :rwaccess;
720 return MR_FILESYS_ACCESS;
722 if ((mr_errcode = prefetch_value(q, argv, cl)) != MR_SUCCESS)
725 if (!strcmp(type, "NFS") || !strcmp(type, "IMAP"))
726 return check_nfs(mach_id, name, rwaccess);
732 /* Verify the arguments, depending on the FStype. Also, if this is an
733 * NFS filesystem, then update any quotas for that filesystem to reflect
737 int setup_ufil(struct query *q, char *argv[], client *cl)
741 EXEC SQL BEGIN DECLARE SECTION;
742 int fid, total, who, ok;
743 char *entity, ftype[FILESYS_TYPE_SIZE + 10], *access;
744 short int total_null;
745 EXEC SQL END DECLARE SECTION;
749 mach_id = *(int *)argv[3];
752 fid = *(int *)argv[0];
756 sprintf(ftype, "fs_access_%s", type);
757 EXEC SQL SELECT COUNT(trans) INTO :ok FROM alias
758 WHERE name = :ftype AND type = 'TYPE' AND trans = :access;
762 return MR_FILESYS_ACCESS;
764 EXEC SQL SELECT type INTO :ftype FROM filesys
765 WHERE filsys_id = :fid;
769 if (!strcmp(type, "NFS") || !strcmp(type, "IMAP"))
771 status = check_nfs(mach_id, name, access);
772 EXEC SQL UPDATE quota SET phys_id = :_var_phys_id
773 WHERE filsys_id = :fid;
778 else if (!strcmp(type, "AFS") && strcmp(strtrim(ftype), "AFS")
779 && strcmp(strtrim(ftype), "ERR"))
782 EXEC SQL DELETE FROM quota
783 WHERE type = 'ANY' AND filsys_id = :fid;
784 EXEC SQL SELECT SUM (quota) INTO :total:total_null FROM quota
785 WHERE filsys_id = :fid AND phys_id != 0;
788 if (!total_null && (total != 0))
790 EXEC SQL INSERT INTO quota (quota, filsys_id, phys_id, entity_id,
791 type, modtime, modby, modwith)
792 VALUES (:total, :fid, 0, 0, 'ANY', SYSDATE, :who, :entity);
799 EXEC SQL UPDATE quota SET phys_id = 0 WHERE filsys_id = :fid;
807 /* Find the NFS physical partition that the named directory is on.
808 * This is done by comparing the dir against the mount point of the
809 * partition. To make sure we get the correct match when there is
810 * more than one, we sort the query in reverse order by dir name.
813 int check_nfs(int mach_id, char *name, char *access)
815 EXEC SQL BEGIN DECLARE SECTION;
816 char dir[NFSPHYS_DIR_SIZE];
818 EXEC SQL END DECLARE SECTION;
824 EXEC SQL DECLARE csr101 CURSOR FOR
825 SELECT nfsphys_id, dir FROM nfsphys
830 EXEC SQL OPEN csr101;
835 EXEC SQL FETCH csr101 INTO :_var_phys_id, :dir;
852 EXEC SQL CLOSE csr101;
859 /* setup_dfil: free any quota records and fsgroup info associated with
860 * a filesystem when it is deleted. Also adjust the allocation numbers.
863 int setup_dfil(struct query *q, char **argv, client *cl)
865 EXEC SQL BEGIN DECLARE SECTION;
866 int id, total, phys_id;
868 EXEC SQL END DECLARE SECTION;
870 id = *(int *)argv[0];
871 EXEC SQL SELECT SUM (quota) INTO :total:none FROM quota
872 WHERE filsys_id = :id;
877 /** What if there are multiple phys_id's per f/s? (bad data) **/
878 EXEC SQL SELECT phys_id INTO :phys_id FROM filesys
879 WHERE filsys_id = :id;
880 EXEC SQL UPDATE nfsphys SET allocated = allocated - :total
881 WHERE nfsphys_id = :phys_id;
884 EXEC SQL DELETE FROM quota WHERE filsys_id = :id;
885 EXEC SQL DELETE FROM fsgroup WHERE filsys_id = :id;
886 EXEC SQL DELETE FROM fsgroup WHERE group_id = :id;
893 /* setup_dnfp: check to see that the nfs physical partition does not have
894 * any filesystems assigned to it before allowing it to be deleted.
897 int setup_dnfp(struct query *q, char **argv, client *cl)
899 EXEC SQL BEGIN DECLARE SECTION;
902 EXEC SQL END DECLARE SECTION;
904 id = *(int *)argv[0];
906 EXEC SQL SELECT count(fs.rowid) INTO :cnt FROM filesys fs, nfsphys np
907 WHERE fs.mach_id = :id AND fs.phys_id = np.nfsphys_id
908 AND np.mach_id = :id AND np.dir = :dir;
917 /* setup_dqot: Remove allocation from nfsphys before deleting quota.
918 * argv[0] = filsys_id
919 * argv[1] = type if "update_quota" or "delete_quota"
920 * argv[2 or 1] = users_id or list_id
923 int setup_dqot(struct query *q, char **argv, client *cl)
925 EXEC SQL BEGIN DECLARE SECTION;
926 int quota, fs, id, physid;
928 EXEC SQL END DECLARE SECTION;
930 fs = *(int *)argv[0];
931 if (!strcmp(q->name, "update_quota") || !strcmp(q->name, "delete_quota"))
934 id = *(int *)argv[2];
939 id = *(int *)argv[1];
942 EXEC SQL SELECT quota INTO :quota FROM quota
943 WHERE type = :qtype AND entity_id = :id AND filsys_id = :fs;
944 EXEC SQL SELECT phys_id INTO :physid FROM filesys
945 WHERE filsys_id = :fs;
946 EXEC SQL UPDATE nfsphys SET allocated = allocated - :quota
947 WHERE nfsphys_id = :physid;
956 * This routine fetches an appropriate value from the numvalues table.
957 * It is a little hack to get around the fact that SQL doesn't let you
958 * do something like INSERT INTO table (foo) VALUES (other_table.bar).
960 * It is called from the query table as (*v->pre_rtn)(q, Argv, cl) or
961 * from within a setup_...() routine with the appropriate arguments.
963 * Correct functioning of this routine may depend on the assumption
964 * that this query is an APPEND.
967 int prefetch_value(struct query *q, char **argv, client *cl)
969 EXEC SQL BEGIN DECLARE SECTION;
970 char *name = q->validate->object_id;
972 EXEC SQL END DECLARE SECTION;
973 int status, limit, argc;
975 /* set next object id, limiting it if necessary */
976 if (!strcmp(name, "unix_uid") || !strcmp(name, "gid"))
977 limit = 1; /* So far as I know, this isn't needed. Just CMA. */
980 if ((status = set_next_object_id(name, q->rtable, limit)) != MR_SUCCESS)
983 /* fetch object id */
984 EXEC SQL SELECT value INTO :value FROM numvalues WHERE name = :name;
987 if (sqlca.sqlerrd[2] != 1)
990 argc = q->argc + q->vcnt; /* end of Argv for APPENDs */
991 sprintf(argv[argc], "%d", value);
996 /* prefetch_filesys():
997 * Fetches the phys_id from filesys based on the filsys_id in argv[0].
998 * Appends the filsys_id and the phys_id to the argv so they can be
999 * referenced in an INSERT into a table other than filesys. Also
1000 * see comments at prefetch_value().
1002 * Assumes the existence of a row where filsys_id = argv[0], since a
1003 * filesys label has already been resolved to a filsys_id.
1005 int prefetch_filesys(struct query *q, char **argv, client *cl)
1007 EXEC SQL BEGIN DECLARE SECTION;
1009 EXEC SQL END DECLARE SECTION;
1012 fid = *(int *)argv[0];
1013 EXEC SQL SELECT phys_id INTO :phid FROM filesys WHERE filsys_id = :fid;
1017 argc = q->argc + q->vcnt;
1018 sprintf(argv[argc++], "%d", phid);
1019 sprintf(argv[argc], "%d", fid);
1028 int setup_ahst(struct query *q, char **argv, client *cl)
1030 EXEC SQL BEGIN DECLARE SECTION;
1031 char *name, oldname[MACHINE_NAME_SIZE], vendor[MACHINE_VENDOR_SIZE];
1032 char model[MACHINE_MODEL_SIZE], os[MACHINE_OS_SIZE];
1033 int value, id, ssaddr, smask, shigh, slow, cnt;
1034 unsigned int saddr, mask, high, low;
1035 EXEC SQL END DECLARE SECTION;
1037 struct in_addr addr;
1039 id = *(int *)argv[0];
1041 if (!strcmp(q->shortname, "uhst"))
1044 EXEC SQL SELECT name, vendor, model, os
1045 INTO :oldname, :vendor, :model, :os
1046 FROM machine WHERE mach_id = :id;
1053 else if (q->version >= 6 && q->version < 8)
1058 /* Sanity check name, vendor, model, and os. */
1059 if ((row == 0 || strcasecmp(argv[1], oldname)) &&
1060 !hostname_check(argv[row]))
1062 if ((row == 0 || strcasecmp(argv[2], vendor)) &&
1063 !hostinfo_check(argv[row + 1], 0))
1065 if ((row == 0 || strcasecmp(argv[3], model)) &&
1066 !hostinfo_check(argv[row + 2], 1))
1068 if ((row == 0 || strcasecmp(argv[4], os)) &&
1069 !hostinfo_check(argv[row + 3], 0))
1072 /* check for duplicate name */
1074 EXEC SQL SELECT count(mach_id) INTO :cnt FROM hostalias
1075 WHERE name = UPPER(:name);
1082 if (!strcmp(argv[9 + row + idx], "unassigned"))
1084 else if (!strcmp(argv[9 + row + idx], "unique"))
1086 if (*(int *)argv[8 + row + idx] == 0)
1093 value = ntohl(inet_addr(argv[9 + row + idx]));
1102 * an address or unique was specified.
1104 id = *(int *)argv[8 + row + idx];
1105 EXEC SQL SELECT saddr, mask, high, low INTO :ssaddr, :smask,
1106 :shigh, :slow FROM subnet WHERE snet_id = :id;
1109 saddr = (unsigned) ssaddr;
1110 mask = (unsigned) smask;
1111 high = (unsigned) shigh;
1112 low = (unsigned) slow;
1116 * someone specified an IP address for the host record
1118 if ((value & mask) != saddr || value < low || value > high)
1121 * run the address argument through inet_addr(). This
1122 * has the effect that any out of bounds host addrs will
1123 * be converted to a valid host addr. We do this now
1124 * so that the uniqueness check works. We should also
1125 * link in an inet_addr() that returns an error for
1128 addr.s_addr = inet_addr(argv[9 + row + idx]);
1129 name = inet_ntoa(addr);
1130 EXEC SQL SELECT count(mach_id) INTO :cnt FROM machine
1131 WHERE address = :name;
1137 * make IP address is unique. If this a modify request
1138 * (row == 1), then we expect one record to exist.
1140 if (row == 0 || (row == 1 && cnt > 1))
1142 if (row == 1 && cnt == 1)
1144 EXEC SQL SELECT mach_id INTO :id FROM machine
1145 WHERE address = :name;
1146 if (id != *(int *)argv[0])
1154 * a "unique" address was specified. Walk through the
1155 * range specified in the network record, return
1156 * error if no room left.
1158 for (id = low; id <= high; id++)
1160 if (((id & 0xff) == 0) || ((id & 0xff) == 255))
1162 addr.s_addr = htonl(id);
1163 name = inet_ntoa(addr);
1164 EXEC SQL SELECT count(mach_id) INTO :cnt FROM machine
1165 WHERE address = :name;
1177 * we have an address in value. Convert it to a string and store it.
1179 addr.s_addr = htonl(value);
1180 strcpy(argv[9 + row + idx], inet_ntoa(addr));
1183 strcpy(argv[9 + row + idx], "unassigned");
1185 /* status checking */
1186 value = atoi(argv[7 + row + idx]);
1187 if (row == 0 && !(value == 1 || value == 0))
1191 id = *(int *)argv[0];
1192 EXEC SQL SELECT status INTO :cnt FROM machine WHERE mach_id = :id;
1197 EXEC SQL UPDATE machine SET statuschange = SYSDATE
1198 WHERE mach_id = :id;
1203 * If this is an update_host query, we're done.
1209 * For an add_host query, allocate and fill in a new machine id,
1210 * and then insert the creator id.
1212 if ((mr_errcode = prefetch_value(q, argv, cl)) != MR_SUCCESS)
1215 sprintf(argv[q->argc + q->vcnt + 1], "%d", cl->client_id);
1223 int setup_ahal(struct query *q, char **argv, client *cl)
1225 EXEC SQL BEGIN DECLARE SECTION;
1228 EXEC SQL END DECLARE SECTION;
1232 if (!hostname_check(argv[0]))
1235 EXEC SQL SELECT count(mach_id) INTO :cnt FROM machine WHERE
1236 name = UPPER(:name);
1245 /* setup_uhha(): Check characters in hwaddr, and make sure it's not
1248 int setup_uhha(struct query *q, char **argv, client *cl)
1250 EXEC SQL BEGIN DECLARE SECTION;
1251 char *hwaddr = argv[1];
1253 EXEC SQL END DECLARE SECTION;
1256 if (*hwaddr && strcasecmp(hwaddr, "unknown"))
1258 for (p = hwaddr; *p; p++)
1265 if (p != hwaddr + 12)
1268 EXEC SQL SELECT COUNT(hwaddr) INTO :count
1269 FROM machine WHERE hwaddr = :hwaddr;
1271 return MR_NOT_UNIQUE;
1277 /* setup_aprn(): Make sure name/duplexname don't conflict with
1278 * anything. If [ANY] was specified for the spooling host, pick the
1279 * least loaded print server that serves this kind of printer.
1281 int setup_aprn(struct query *q, char **argv, client *cl)
1285 EXEC SQL BEGIN DECLARE SECTION;
1286 int mid, usage, count;
1287 char types[STRINGS_STRING_SIZE], *hwaddr, *name, *duplexname, *oldname;
1288 EXEC SQL END DECLARE SECTION;
1290 /* Check for aprn or uprn. */
1291 if (q->type == APPEND)
1296 name = argv[PRN_NAME + row];
1297 duplexname = argv[PRN_DUPLEXNAME + row];
1304 if (q->type == APPEND)
1306 EXEC SQL SELECT COUNT(name) INTO :count FROM printers
1307 WHERE name = :name OR duplexname = :name;
1311 EXEC SQL SELECT COUNT(name) INTO :count FROM printers
1312 WHERE ( name = :name OR duplexname = :name )
1313 AND name != :oldname;
1318 return MR_NOT_UNIQUE;
1323 if (q->type == APPEND)
1325 EXEC SQL SELECT COUNT(name) INTO :count FROM printers
1326 WHERE name = :duplexname OR duplexname = :duplexname;
1330 EXEC SQL SELECT COUNT(name) INTO :count FROM printers
1331 WHERE ( name = :duplexname OR duplexname = :duplexname )
1332 AND name != :oldname;
1338 return MR_NOT_UNIQUE;
1341 if (!strcmp(name, duplexname))
1342 return MR_NOT_UNIQUE;
1344 mid = *(int *)argv[PRN_RM + row];
1347 EXEC SQL DECLARE csr_rm CURSOR FOR
1348 SELECT ps.mach_id, s.string FROM printservers ps, strings s
1350 ( SELECT mach_id FROM serverhosts WHERE service = 'PRINT'
1352 AND ps.printer_types = s.string_id;
1355 EXEC SQL OPEN csr_rm;
1361 EXEC SQL FETCH csr_rm INTO :mid, :types;
1365 for (p = strtok(types, ", "); p; p = strtok(NULL, ", "))
1367 if (!strcasecmp(argv[PRN_TYPE + row], p))
1369 EXEC SQL SELECT COUNT(name) INTO :usage
1370 FROM printers WHERE rm = :mid;
1372 if (best < 0 || usage < best)
1375 *(int *)argv[PRN_RM + row] = mid;
1381 EXEC SQL CLOSE csr_rm;
1390 EXEC SQL SELECT mach_id INTO :mid FROM printservers
1391 WHERE mach_id = :mid;
1399 int setup_dpsv(struct query *q, char **argv, client *cl)
1402 EXEC SQL BEGIN DECLARE SECTION;
1404 EXEC SQL END DECLARE SECTION;
1406 id = *(int *)argv[0];
1408 EXEC SQL SELECT COUNT(rm) INTO :cnt FROM printers
1416 int setup_dcon(struct query *q, char *argv[], client *cl)
1418 EXEC SQL BEGIN DECLARE SECTION;
1420 char containername[CONTAINERS_NAME_SIZE];
1421 EXEC SQL END DECLARE SECTION;
1423 id = *(int *)argv[0];
1424 /* check to see if there are machines in this container */
1425 EXEC SQL SELECT COUNT(mach_id) INTO :cnt FROM mcntmap
1430 /* check to see if there are subcontainers in this container */
1432 /* get the container name */
1434 EXEC SQL SELECT name INTO :containername
1438 /* trim off the trailing spaces */
1439 strcpy(containername, strtrim(containername));
1441 EXEC SQL SELECT COUNT(cnt_id) INTO :cnt FROM containers
1442 WHERE LOWER(name) LIKE LOWER(:containername || '/' || '%');
1452 int setup_scli(struct query *q, char *argv[], client *cl)
1454 EXEC SQL BEGIN DECLARE SECTION;
1455 int cnt_id, list_id;
1456 EXEC SQL END DECLARE SECTION;
1458 cnt_id = *(int *)argv[0];
1459 /* Check if someone has already set the list for this container */
1460 EXEC SQL SELECT list_id INTO :list_id FROM containers
1461 WHERE cnt_id = :cnt_id;
1472 * validate the rfc1035/rfc1123-ness of a hostname
1475 int hostname_check(char *name)
1480 /* Sanity check name: must contain only letters, numerals, and
1481 * hyphen, and not start or end with a hyphen. Also make sure no
1482 * label (the thing the .s seperate) is longer than 63 characters,
1486 for (p = name, count = 0; *p; p++)
1489 if ((!isalnum(*p) && *p != '-' && *p != '.') ||
1490 (*p == '-' && p[1] == '.'))
1501 if (*(p - 1) == '-')
1506 int hostinfo_check(char *info, int num)
1513 /* Sanity check host hostinfo: must start with a letter (or number
1514 * if num is true), contain only letters, numerals, and hyphen, and
1515 * not end with a hyphen.
1518 if (!isalpha(*info) && (!num || !isdigit(*info)))
1520 for (p = info; *p; p++)
1522 if ((!isalnum(*p) && *p != '-' && *p != '.') ||
1523 (*p == '-' && p[1] == '.'))
1526 if (!isalnum(*(p - 1)))
1530 int setup_acon(struct query *q, char *argv[], client *cl)
1532 EXEC SQL BEGIN DECLARE SECTION;
1533 char containername[CONTAINERS_NAME_SIZE];
1534 EXEC SQL END DECLARE SECTION;
1538 memset(containername, 0, sizeof(containername));
1539 strcpy(containername, argv[0]);
1540 ptr = strrchr(containername, '/');
1541 /* sub container, check for parents */
1545 EXEC SQL SELECT * FROM containers
1546 WHERE lower(name) = lower(:containername);
1547 if (sqlca.sqlerrd[2] != 1)
1548 return MR_CONTAINER_NO_PARENT;
1551 if ((mr_errcode = prefetch_value(q, argv, cl)) != MR_SUCCESS)
1557 int check_mailman_listname(char *name, const char *suffix)
1560 EXEC SQL BEGIN DECLARE SECTION;
1562 EXEC SQL END DECLARE SECTION;
1564 p = strstr(name, suffix);
1567 if (strlen(name) == (p - name + strlen(suffix)))
1569 /* list is of the form "name-suffix" */
1571 EXEC SQL SELECT COUNT(name) INTO :cnt FROM list
1572 WHERE name = SUBSTR(:name, 1, :i) AND mailman = 1;