]> andersk Git - moira.git/blob - server/qrtn.qc
Move zephyr notification stuff into call to critical_alert
[moira.git] / server / qrtn.qc
1 /*
2  *      $Source$
3  *      $Author$
4  *      $Header$
5  *
6  *      Copyright (C) 1987, 1988 by the Massachusetts Institute of Technology
7  * 
8  */
9
10 #ifndef lint
11 static char *rcsid_qrtn_qc = "$Header$";
12 #endif lint
13
14 #include "query.h"
15 #include "sms_server.h"
16
17 char *Argv[16];
18
19 static int ingres_errno = 0;
20 extern char *whoami;
21 extern FILE *journal;
22
23 #define INGRES_BAD_INT 4111
24 #define INGRES_BAD_DATE 4302
25 #define INGRES_DEADLOCK 4700
26
27 /*
28  * ingerr: (supposedly) called when Ingres indicates an error.
29  * I have not yet been able to get this to work to intercept a
30  * database open error.
31  */
32
33 static int ingerr(num)
34     int *num;
35 {
36     char buf[256];
37
38     switch (*num) {
39     case INGRES_BAD_INT:
40         ingres_errno = SMS_INTEGER;
41         break;
42     case INGRES_BAD_DATE:
43         ingres_errno = SMS_DATE;
44         break;
45     case INGRES_DEADLOCK:
46         ingres_errno = SMS_DEADLOCK;
47         break;
48     default:
49         ingres_errno = SMS_INGRES_ERR;
50         com_err(whoami, SMS_INGRES_ERR, " code %d\n", *num);
51         critical_alert("SMS", "SMS server encountered INGRES ERROR %d", *num);
52         return (*num);
53     }
54     return (0);
55 }
56
57 int sms_open_database()
58 {
59     register int i;
60     char *malloc();
61
62     /* initialize local argv */
63     for (i = 0; i < 16; i++)
64         Argv[i] = malloc(ARGLEN);
65
66     IIseterr(ingerr);
67         
68     ingres_errno = 0;
69         
70     /* open the database */
71 ##  ingres sms
72     return ingres_errno;
73 }
74
75 int sms_close_database()
76 {
77 ##  exit
78 }
79
80 sms_check_access(cl, name, argc, argv_ro)
81     client *cl;
82     char *name;
83     int argc;
84     char *argv_ro[];
85 {
86     struct query *q;
87     struct query *get_query_by_name();
88
89     q = get_query_by_name(name, cl->args->sms_version_no);
90     if (q == (struct query *)0)
91         return(SMS_NO_HANDLE);
92
93     return(sms_verify_query(cl, q, argc, argv_ro));
94 }
95
96 sms_process_query(cl, name, argc, argv_ro, action, actarg)
97     client *cl;
98     char *name;
99     int argc;
100     char *argv_ro[];
101     int (*action)();
102     char *actarg;
103 {
104     register struct query *q;
105     register int status;
106     register struct validate *v;
107     char qual[256];
108     char sort[32];
109     char *pqual;
110     char *psort;
111 ##  char *table;
112     struct save_queue *sq;
113     struct query *get_query_by_name();
114     int sq_save_args();
115     struct save_queue *sq_create();
116     char *build_sort();
117
118     /* list queries command */
119     if (!strcmp(name, "_list_queries")) {
120         list_queries(cl->args->sms_version_no, action, actarg);
121         return(SMS_SUCCESS);
122     }
123
124     /* help query command */
125     if (!strcmp(name, "_help")) {
126         if (argc < 1)
127             return(SMS_ARGS);
128         q = get_query_by_name(argv_ro[0], cl->args->sms_version_no);
129         if (q == (struct query *)0) return(SMS_NO_HANDLE);
130         help_query(q, action, actarg);
131         return(SMS_SUCCESS);
132     }
133
134     /* get query structure, return error if named query does not exist */
135     q = get_query_by_name(name, cl->args->sms_version_no);
136     if (q == (struct query *)0) return(SMS_NO_HANDLE);
137     v = q->validate;
138
139     if (q->type != RETRIEVE)
140 ##      begin transaction
141
142     /* setup argument vector, verify access and arguments */
143     if ((status = sms_verify_query(cl, q, argc, argv_ro)) != SMS_SUCCESS)
144         goto out;
145
146     /* perform any special query pre-processing */
147     if (v && v->pre_rtn) {
148         status = (*v->pre_rtn)(q, Argv, cl, 0);
149         if (status != SMS_SUCCESS)
150             goto out;
151     }
152
153     switch (q->type) {
154     case RETRIEVE:
155         /* for queries that do not permit wildcarding, check if row
156            uniquely exists */
157         if (v && v->field) {
158             status = validate_row(q, Argv, v);
159             if (status != SMS_EXISTS) break;
160         }
161
162         /* build "where" clause if needed */
163         if (q->qual) {
164             build_qual(q->qual, q->argc, Argv, qual);
165             pqual = qual;
166         } else {
167             pqual = 0;
168         }
169
170         /* build "sort" clause if needed */
171         if (v && v->valobj) {
172             psort = build_sort(v, sort);
173         } else {
174             psort = 0;
175         }
176
177         /* if there is a followup routine, then we must save the results */
178         /* of the first query for use by the followup routine */
179         /* if q->rvar = NULL, perform post_rtn only */
180         if (q->rvar) {
181             if (v && v->post_rtn) {
182                 sq = sq_create();
183                 status = do_retrieve(q, pqual, psort, sq_save_args, sq);
184                 if (status != SMS_SUCCESS) {
185                     sq_destroy(sq);
186                     break;
187                 }
188                 status = (*v->post_rtn)(q, sq, v, action, actarg, cl);
189             } else {
190                 /* normal retrieve */
191                 status = do_retrieve(q, pqual, psort, action, actarg);
192             }
193             if (status != SMS_SUCCESS) break;
194             table = q->rtable;
195 ##          repeat replace tblstats (retrieves = tblstats.retrieves + 1)
196 ##                 where tblstats.#table = @table
197         } else {
198             status = (*v->post_rtn)(q, Argv, cl, action, actarg);
199         }
200
201         break;
202
203     case UPDATE:
204         /* see if row already exists */
205         if (v->field) {
206             status = validate_row(q, Argv, v);
207             if (status != SMS_EXISTS) break;
208         }
209
210         /* build "where" clause and perform update */
211         /* if q->rvar = NULL, perform post_rtn only */
212         if (q->rvar) {
213             build_qual(q->qual, q->argc, Argv, qual);
214             status = do_update(q, &Argv[q->argc], qual, action, actarg);
215             if (status != SMS_SUCCESS) break;
216             table = q->rtable;
217             if (strcmp(q->shortname, "sshi") && strcmp(q->shortname, "ssif")) {
218 ##              repeat replace tblstats (updates = tblstats.updates + 1,
219 ##                                       modtime = "now")
220 ##                  where tblstats.#table = @table
221             }
222         }
223
224         /* execute followup routine (if any) */
225         if (v->post_rtn) status = (*v->post_rtn)(q, Argv, cl);
226
227         break;
228
229     case APPEND:
230         /* see if row already exists */
231         if (v->field) {
232             status = validate_row(q, Argv, v);
233             if (status != SMS_NO_MATCH) break;
234         }
235
236         /* increment id number if necessary */
237         if (v->object_id) {
238             status = set_next_object_id(v->object_id, q->rtable);
239             if (status != SMS_SUCCESS) break;
240         }
241
242         /* build "where" clause if needed */
243         if (q->qual) {
244             build_qual(q->qual, q->argc, Argv, qual);
245             pqual = qual;
246         } else {
247             pqual = 0;
248         }
249
250         /* perform the append */
251         /* if q->rvar = NULL, perform post_rtn only */
252         if (q->rvar) {
253             status = do_append(q, &Argv[q->argc], pqual, action, actarg);
254             if (status != SMS_SUCCESS) break;
255             table = q->rtable;
256 ##          repeat replace tblstats (appends = tblstats.appends + 1,
257 ##                                   modtime = "now")
258 ##              where tblstats.#table = @table
259         }
260         
261         /* execute followup routine */
262         if (v->post_rtn) status = (*v->post_rtn)(q, Argv, cl);
263         break;
264
265     case DELETE:
266         /* see if row already exists */
267         if (v->field) {
268             status = validate_row(q, Argv, v);
269             if (status != SMS_EXISTS) break;
270         }
271
272         /* build "where" clause and perform delete */
273         /* if q->rvar = NULL, perform post_rtn only */
274         if (q->rvar) {
275             build_qual(q->qual, q->argc, Argv, qual);
276             status = do_delete(q, qual, action, actarg);
277             if (status != SMS_SUCCESS) break;
278             table = q->rtable;
279 ##          repeat replace tblstats (deletes = tblstats.deletes + 1,
280 ##                                   modtime = "now")
281 ##              where tblstats.#table = @table
282         }
283
284         /* execute followup routine */
285         if (v->post_rtn) status = (*v->post_rtn)(q, Argv, cl);
286         break;
287
288     }
289
290 out:
291     if (q->type != RETRIEVE) {
292         if (status == SMS_SUCCESS) {
293 ##          end transaction     /* commit to this */
294             if (journal) {
295                 char buf[1024], *bp;
296                 int i;
297                 extern time_t now;
298
299                 fprintf(journal, "%% %s %s %s",
300                         cl->clname, cl->entity, ctime(&now));
301                 fprintf(journal, "%s[%d] ", q->name, cl->args->sms_version_no);
302                 for (i = 0; i < argc; i++) {
303                     if (i != 0) {
304                         putc(' ', journal);
305                     }
306                     requote(buf, argv_ro[i], sizeof(buf));
307                     fputs(buf, journal);
308                 }
309                 putc('\n', journal);
310                 fflush(journal);
311             }
312         } else {
313 ##          abort               /* it never happened */
314         }
315     }
316
317     if (status != SMS_SUCCESS && log_flags & LOG_RES)
318         com_err(whoami, status, " (Query failed)");
319     return(status);
320 }
321
322 build_qual(fmt, argc, argv, qual)
323         char *fmt;
324         int argc;
325         char *argv[];
326         char *qual;
327 {
328     register char *c;
329     register int i;
330     char *args[4];
331     char *index();
332
333     c = fmt;
334     for (i = 0; i < argc; i++) {
335         c = index(c, '%');
336         if (c++ == (char *)0) return(SMS_ARGS);
337         if (*c == 's')
338             args[i] = argv[i];
339         else if (*c == 'd')
340             *(int *)&args[i] = *(int *)argv[i]; /* sigh */
341         else
342             return(SMS_INGRES_ERR);
343     }
344
345     switch (argc) {
346     case 0:
347         strcpy(qual, fmt);
348         break;
349
350     case 1:
351         sprintf(qual, fmt, args[0]);
352         break;
353
354     case 2:
355         sprintf(qual, fmt, args[0], args[1]);
356         break;
357
358     case 3:
359         sprintf(qual, fmt, args[0], args[1], args[2]);
360         break;
361
362     case 4:
363         sprintf(qual, fmt, args[0], args[1], args[2], args[3]);
364         break;
365     }
366     return(SMS_SUCCESS);
367 }
368
369 char *
370 build_sort(v, sort)
371     register struct validate *v;
372     char *sort;
373 {
374     register struct valobj *vo;
375     register int n;
376     char elem[16];
377
378     n = v->objcnt;
379     vo = v->valobj;
380     *sort = 0;
381
382     while (--n >= 0) {
383         if (vo->type == V_SORT) {
384             sprintf(elem, "RET_VAR%d", vo->index + 1);
385             if (*sort) strcat(sort, ", ");
386             strcat(sort, elem);
387         }
388         vo++;
389     }
390
391     return ((*sort) ? sort : 0);
392 }
393
394
395 /* Build arguement vector, verify query and arguments */
396
397 sms_verify_query(cl, q, argc, argv_ro)
398     client *cl;
399     struct query *q;
400     int argc;
401     char *argv_ro[];
402 {
403     register int argreq;
404     register int status;
405     register struct validate *v = q->validate;
406     register int i;
407     register int privileged = 0;
408
409     /* copy the arguments into a local argv that we can modify */
410     for (i = 0; i < argc; i++) {
411         if (strlen(argv_ro[i]) < ARGLEN)
412             strcpy(Argv[i], argv_ro[i]);
413         else
414             return(SMS_ARG_TOO_LONG);
415     }
416
417     /* check initial query access */
418     status = check_query_access(q, Argv, cl);
419     if (status != SMS_SUCCESS && status != SMS_PERM)
420         return(status);
421     if (status == SMS_SUCCESS)
422         privileged++;
423
424     /* check argument count */
425     argreq = q->argc;
426     if (q->type == UPDATE || q->type == APPEND) argreq += q->vcnt;
427     if (argc != argreq) return(SMS_ARGS);
428
429     /* validate arguments */
430     if (v && v->valobj) {
431         status = validate_fields(q, Argv, v->valobj, v->objcnt);
432         if (status != SMS_SUCCESS) return(status);
433     }
434
435     /* perform special query access check */
436     if (!privileged && v && v->acs_rtn) {
437         status = (*v->acs_rtn)(q, Argv, cl);
438         if (status != SMS_SUCCESS && status != SMS_PERM)
439             return(status);
440         if (status == SMS_SUCCESS)
441             privileged++;
442     }
443
444     return(privileged ? SMS_SUCCESS : SMS_PERM);
445 }
446
447 check_query_access(q, argv, cl)
448     struct query *q;
449     char *argv[];
450     client *cl;
451 ##{
452 ##  char *name;
453 ##  int acl_id;
454 ##  int exists;
455 ##  int rowcount;
456 ##  int errorno;
457 ##  static int def_uid;
458     int status;
459     int client_id;
460     char *client_type;
461
462     /* get query access control list */
463     name = q->shortname;
464 ##  repeat retrieve (acl_id = capacls.list_id) where capacls.tag = @name
465 ##  inquire_equel (rowcount = "rowcount", errorno = "errorno")
466     if (errorno != 0) return(SMS_INGRES_ERR);
467     if (rowcount == 0) return(SMS_PERM);
468
469     /* initialize default uid */
470     if (def_uid == 0) {
471 ##      retrieve (def_uid = users.users_id) where users.login = "default"
472     }
473
474     /* check for default access */
475 ##  range of m is members
476 ##  repeat retrieve (exists = any(m.#member_id where m.list_id = @acl_id and
477 ##                   m.member_type = "USER" and m.#member_id = def_uid))
478     if (exists) return(SMS_SUCCESS);
479
480     /* parse client name */
481     status = get_client(cl, &client_type, &client_id);
482     if (status != SMS_SUCCESS) return(status);
483
484     /* see if client is in the list (or any of its sub-lists) */
485     exists = find_member("LIST", acl_id, client_type, client_id, 0);
486     return ((exists) ? SMS_SUCCESS : SMS_PERM);
487 ##}
488
489 get_client(cl, client_type, client_id)
490     client *cl;
491     char **client_type;
492     int *client_id;
493 ##{
494     struct krbname *krb;
495 ##  int member_id;
496 ##  char *name;
497 ##  int rowcount;
498
499     if (cl->clname == NULL)
500         return SMS_PERM;
501     
502     /* for now ignore instances */
503     krb = &cl->kname;
504
505     /* if client is from local realm, get users_id */
506     if (!strcmp(krb->realm, krb_realm)) {
507         *client_id = cl->users_id;
508         *client_type = "USER";
509         return(SMS_SUCCESS);
510     }
511
512     /* otherwise use string_id */
513     name = cl->clname;
514 ##  repeat retrieve (member_id = strings.string_id) 
515 ##      where strings.string = @name
516         
517     /* make sure we found a users or string id */
518 ##  inquire_equel (rowcount = "rowcount")
519     if (rowcount == 0) return(SMS_PERM);
520
521     *client_type = "STRING";
522     *client_id = member_id;
523     return(SMS_SUCCESS);
524 ##}
525
526 ##find_member(list_type, list_id, member_type, member_id, sq)
527     char *list_type;
528 ##  int list_id;
529 ##  char *member_type;
530 ##  int member_id;
531     struct save_queue *sq;
532 ##{
533 ##  int exists;
534 ##  int sublist;
535     int child;
536     struct save_queue *sq_create();
537
538     /* see if client is a direct member of list */
539 ##  repeat retrieve (exists = any(m.#member_id where 
540 ##                                m.#list_id = @list_id and
541 ##                                m.#member_type = @member_type and 
542 ##                                m.#member_id = @member_id))
543     if (exists) return(1);
544
545     /* are there any sub-lists? */
546 ##  repeat retrieve (exists = any(m.#member_id where m.#list_id = @list_id and
547 ##                   m.#member_type = "LIST"))
548     if (!exists) return(0);
549
550     /* yes; now recurse through sublists */
551
552     /* create a save queue */
553     if (sq == (struct save_queue *)0) {
554         sq = sq_create();
555         child = 0;
556     } else {
557         child = 1;
558     }
559
560     /* save all sublist ids */
561 ##  range of m is members
562 ##  retrieve (sublist = m.#member_id) 
563 ##      where m.#list_id = list_id and m.#member_type = "LIST"
564 ##  {
565          sq_save_unique_data(sq, sublist);
566 ##  }
567
568     if (child) return(0);
569
570     /* at top-level, check sub-lists for client (breadth-first search) */
571     while (sq_get_data(sq, &sublist)) {
572         exists = find_member(list_type, sublist, member_type, member_id, sq);
573         if (exists) {
574             sq_destroy(sq);
575             return(1);
576         }
577     }
578     sq_destroy(sq);
579     return(0);
580 ##}
581
582
583 do_retrieve(q, pqual, psort, action, actarg)
584     register struct query *q;
585     char *pqual;
586     char *psort;
587     int (*action)();
588     char *actarg;
589 ##{
590 ##  char *rvar;
591 ##  char *rtable;
592 ##  char *cqual;
593 ##  char *csort;
594 ##  int rowcount;
595 ##  int errorno;
596     static char **vaddrs = (char **)NULL;
597
598     if (!vaddrs) {
599         register int i;
600
601         if ((vaddrs = (char **)malloc(sizeof(char *) * QMAXARGS)) == NULL) {
602             com_err(whoami, SMS_NO_MEM, "setting up static argv");
603             exit(1);
604         }
605         for (i = 0; i < QMAXARGS; i++) {
606             if ((vaddrs[i] = malloc(QMAXARGSIZE)) == NULL) {
607                 com_err(whoami, SMS_NO_MEM, "setting up static argv");
608                 exit(1);
609             }
610         }
611     }
612
613     if (q->rvar) {
614         rvar = q->rvar;
615         rtable = q->rtable;
616 ##      range of rvar is rtable
617     }
618
619     if (psort) {
620         csort = psort;
621         if (pqual) {
622             cqual = pqual;
623 ##          retrieve unique (param (q->tlist, vaddrs)) where cqual
624 ##                   sort by csort
625 ##          {
626                  (*action)(q->vcnt, vaddrs, actarg);
627 ##          }
628         } else {
629 ##          retrieve unique (param (q->tlist, vaddrs))
630 ##                   sort by csort
631 ##          {
632                  (*action)(q->vcnt, vaddrs, actarg);
633 ##          }
634         }
635
636     } else {
637         if (pqual) {
638             cqual = pqual;
639 ##          retrieve unique (param (q->tlist, vaddrs)) where cqual
640 ##          {
641                  (*action)(q->vcnt, vaddrs, actarg);
642 ##          }
643         } else {
644 ##          retrieve unique (param (q->tlist, vaddrs))
645 ##          {
646                  (*action)(q->vcnt, vaddrs, actarg);
647 ##          }
648         }
649     }
650
651 ##  inquire_equel (rowcount = "rowcount", errorno = "errorno")
652     if (errorno != 0) return(SMS_INGRES_ERR);
653     return ((rowcount == 0) ? SMS_NO_MATCH : SMS_SUCCESS);
654 ##}
655
656 do_update(q, argv, qual, action, actarg)
657     register struct query *q;
658     char *argv[];
659     char *qual;
660     int (*action)();
661     char *actarg;
662 ##{
663 ##  char *rvar;
664 ##  char *rtable;
665 ##  char *cqual;
666 ##  int errorno;
667       
668     rvar = q->rvar;
669     rtable = q->rtable;
670 ##  range of rvar is rtable
671
672     cqual = qual;
673 ##  replace rvar (param (q->tlist, argv))
674 ##  where cqual
675
676 ##  inquire_equel (errorno = "errorno")
677     if (errorno == INGRES_BAD_INT)
678         return(SMS_INTEGER);
679     else if (errorno != 0)
680         return(SMS_INGRES_ERR);
681     return(SMS_SUCCESS);
682 ##}
683
684 do_append(q, argv, pqual, action, actarg)
685     register struct query *q;
686     char *argv[];
687     char *pqual;
688     int (*action)();
689     char *actarg;
690 ##{
691 ##  char *rvar;
692 ##  char *rtable;
693 ##  char *cqual;
694 ##  int errorno;
695
696     rvar = q->rvar;
697     rtable = q->rtable;
698 ##  range of rvar is rtable
699
700     if (pqual) {
701         cqual = pqual;
702 ##      append to rtable (param (q->tlist, argv)) where cqual
703     } else {
704 ##      append to rtable (param (q->tlist, argv))
705     }
706
707 ##  inquire_equel (errorno = "errorno")
708     if (errorno == INGRES_BAD_INT)
709         return(SMS_INTEGER);
710     else if (errorno != 0)
711         return(SMS_INGRES_ERR);
712     return(SMS_SUCCESS);
713 ##}
714
715 do_delete(q, qual, action, actarg)
716     register struct query *q;
717     char *qual;
718     int (*action)();
719     char *actarg;
720 ##{
721 ##  char *rvar;
722 ##  char *rtable;
723 ##  char *cqual;
724 ##  int errorno;
725
726     rvar = q->rvar;
727     rtable = q->rtable;
728 ##  range of rvar is rtable
729
730     cqual = qual;
731 ##  delete rvar where cqual
732
733 ##  inquire_equel (errorno = "errorno")
734     if (errorno != 0) return(SMS_INGRES_ERR);
735     return(SMS_SUCCESS);
736 ##}
737
738
739 /**
740  ** set_next_object_id - set next object id in values table
741  **
742  ** Inputs: object - object name in values table and in objects
743  **         table - name of table objects are found in
744  **
745  ** - called before an APPEND operation to set the next object id to
746  **   be used for the new record to the next free value
747  **
748  **/
749
750 set_next_object_id(object, table)
751     char *object;
752     char *table;
753 ##{
754 ##  char *name, *tbl;
755 ##  int rowcount, exists, value;
756
757     name = object;
758     tbl = table;
759 ##  range of v is values
760 ##  repeat retrieve (value = v.#value) where v.#name = @name
761 ##  inquire_equel(rowcount = "rowcount")
762     if (rowcount != 1)
763         return(SMS_NO_ID);
764
765 ##  retrieve (exists = any(tbl.name where tbl.name = value))
766 ##  inquire_equel(rowcount = "rowcount")
767     if (rowcount != 1)
768         return(SMS_NO_ID);
769     while (exists) {
770         value++;
771         if (value > MAX_ID_VALUE)
772             value = MIN_ID_VALUE;
773 ##      retrieve (exists = any(tbl.name where tbl.name = value))
774     }
775
776     if (LOG_RES)
777         com_err(whoami, 0, "setting ID %s to %d", name, value);
778 ##  repeat replace v (#value = @value) where v.#name = @name
779     return(SMS_SUCCESS);
780 ##}
781
782
783 /* For now this just checks the argc's.  It should also see that there
784  * are no duplicate names.
785  */
786
787 sanity_check_queries()
788 {
789     register int i;
790     int maxv = 0, maxa = 0;
791     extern int QueryCount1, QueryCount2;
792     extern struct query Queries1[], Queries2[];
793 #define MAX(x,y) ((x) > (y) ? (x) : (y))
794
795     for (i = 0; i < QueryCount1; i++) {
796         maxv = MAX(maxv, Queries1[i].vcnt);
797         maxa = MAX(maxa, Queries1[i].argc);
798     }
799     for (i = 0; i < QueryCount2; i++) {
800         maxv = MAX(maxv, Queries2[i].vcnt);
801         maxa = MAX(maxa, Queries2[i].argc);
802     }
803     if (MAX(maxv, maxa) > QMAXARGS) {
804         com_err(whoami, 0, "A query has more args than QMAXARGS");
805         exit(1);
806     }
807 }
808
809
810 /*
811  * Local Variables:
812  * mode: c
813  * c-indent-level: 4
814  * c-continued-statement-offset: 4
815  * c-brace-offset: -4
816  * c-argdecl-indent: 4
817  * c-label-offset: -4
818  * End:
819  */
820
This page took 0.11952 seconds and 5 git commands to generate.