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