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