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