source: trunk/third/moira/server/qrtn.pc @ 23882

Revision 23882, 25.1 KB checked in by broder, 15 years ago (diff)
In moira: * New upstream release * Build and install libmoira as a shared library. (Trac: #70) * Drop most of the krb4 patch - it's been incorporated upstream. * Install the Moira development headers by patching the relevant Makefiles, instead of in the debian/rules file.
Line 
1/* $Id: qrtn.pc,v 2.30 2009-06-01 21:05:02 zacheiss Exp $
2 *
3 * Query-processing routines
4 *
5 * Copyright (C) 1987-1998 by the Massachusetts Institute of Technology
6 * For copying and distribution information, please see the file
7 * <mit-copyright.h>.
8 *
9 */
10
11#include <mit-copyright.h>
12#include "mr_server.h"
13#include "qrtn.h"
14#include "query.h"
15
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19
20EXEC SQL INCLUDE sqlca;  /* SQL Communications Area */
21EXEC SQL INCLUDE sqlda;  /* SQL Descriptor Area */
22
23RCSID("$Header: /afs/athena.mit.edu/astaff/project/moiradev/repository/moira/server/qrtn.pc,v 2.30 2009-06-01 21:05:02 zacheiss Exp $");
24
25SQLDA *mr_sqlda;
26EXEC SQL BEGIN DECLARE SECTION;
27char stmt_buf[MR_STMTBUF_LEN];
28int proxy_acl;
29EXEC SQL END DECLARE SECTION;
30
31char *Argv[QMAXARGS];
32extern char *table_name[];
33extern char *sqlbuffer[QMAXARGS];
34
35int dbms_errno = 0;
36int mr_errcode = 0;
37EXEC SQL BEGIN DECLARE SECTION;
38int query_timeout = 30;
39char *database = "moira";
40EXEC SQL END DECLARE SECTION;
41extern char *whoami;
42extern FILE *journal;
43extern int QueryCount, max_version;
44extern struct query Queries[];
45
46/* Put this in a variable so that we can patch it if necessary */
47int max_row_count = 8192;
48
49int mr_verify_query(client *cl, struct query *q, int argc, char *argv_ro[]);
50int do_retrieve(struct query *q, char *pqual,
51                int (*action)(int, char *[], void *), void *actarg);
52int do_update(struct query *q, char *argv[], char *qual,
53              int (*action)(int, char *[], void *), void *actarg);
54int do_append(struct query *q, char *argv[], char *pqual,
55              int (*action)(int, char *[], void *), void *actarg);
56int do_delete(struct query *q, char *qual,
57              int (*action)(int, char *[], void *), void *actarg);
58void build_sql_stmt(char *result_buf, char *cmd, char *targetlist,
59                    char *argv[], char *qual);
60
61SQLDA *mr_alloc_sqlda(void);
62void sqlglm(char *, int *, int *);
63
64/*
65 * dbmserr: Called when the DBMS indicates an error.
66 */
67
68void dbmserr(void)
69{
70  EXEC SQL BEGIN DECLARE SECTION;
71  char err_msg[256];
72  EXEC SQL END DECLARE SECTION;
73  int bufsize = 256, msglength = 0;
74
75  dbms_errno = -sqlca.sqlcode;
76  mr_errcode = MR_DBMS_ERR;
77  com_err(whoami, MR_DBMS_ERR, " code %d\n", dbms_errno);
78  sqlglm(err_msg, &bufsize, &msglength);
79  err_msg[msglength] = 0;
80  com_err(whoami, 0, "SQL error text = %s", err_msg);
81  critical_alert(whoami, "MOIRA", "Moira server encountered DBMS ERROR %d\n%s",
82                 dbms_errno, err_msg);
83}
84
85/* This is declarative, not executed.  Applies from here on, in this file. */
86EXEC SQL WHENEVER SQLERROR DO dbmserr();
87
88int mr_open_database(void)
89{
90  int i;
91  static int first_open = 1;
92
93  if (first_open)
94    {
95      first_open = 0;
96
97      /* initialize local argv */
98      for (i = 0; i < QMAXARGS; i++)
99        Argv[i] = xmalloc(MAX_FIELD_WIDTH);
100
101      mr_sqlda = mr_alloc_sqlda();
102
103      incremental_init();
104    }
105
106  dbms_errno = 0;
107  mr_errcode = 0;
108
109  /* open the database */
110  EXEC SQL CONNECT :database IDENTIFIED BY :database;
111
112  if (dbms_errno)
113    return mr_errcode;
114
115  EXEC SQL SELECT list_id INTO :proxy_acl FROM capacls
116    WHERE capability = 'proxy';
117  if (dbms_errno)
118    return mr_errcode;
119
120  return MR_SUCCESS;
121}
122
123void mr_close_database(void)
124{
125  EXEC SQL COMMIT RELEASE;
126}
127
128int mr_check_access(client *cl, char *name, int argc, char *argv_ro[])
129{
130  struct query *q;
131
132  dbms_errno = 0;
133  mr_errcode = 0;
134
135  q = get_query_by_name(name, cl->version);
136  if (!q)
137    return MR_NO_HANDLE;
138
139  return mr_verify_query(cl, q, argc, argv_ro);
140}
141
142int mr_process_query(client *cl, char *name, int argc, char *argv_ro[],
143                     int (*action)(int, char *[], void *), void *actarg)
144{
145  struct query *q;
146  int status;
147  struct validate *v;
148  char *qual = NULL;
149  EXEC SQL BEGIN DECLARE SECTION;
150  char *table;
151  EXEC SQL END DECLARE SECTION;
152  struct save_queue *sq;
153
154  dbms_errno = 0;
155  mr_errcode = 0;
156
157  /* list queries command */
158  if (!strcmp(name, "_list_queries"))
159    {
160      list_queries(cl, action, actarg);
161      return MR_SUCCESS;
162    }
163
164  /* help query command */
165  if (!strcmp(name, "_help"))
166    {
167      if (argc < 1)
168        return MR_ARGS;
169      q = get_query_by_name(argv_ro[0], cl->version);
170      if (!q)
171        return MR_NO_HANDLE;
172      help_query(q, action, actarg);
173      return MR_SUCCESS;
174    }
175
176  /* get query structure, return error if named query does not exist */
177  q = get_query_by_name(name, cl->version);
178  if (!q)
179    return MR_NO_HANDLE;
180  v = q->validate;
181
182  /* setup argument vector, verify access and arguments */
183  if ((status = mr_verify_query(cl, q, argc, argv_ro)) != MR_SUCCESS)
184    goto out;
185
186  /* perform any special query pre-processing */
187  if (v && v->pre_rtn)
188    {
189      status = (*v->pre_rtn)(q, Argv, cl);
190      if (status != MR_SUCCESS)
191        goto out;
192    }
193
194  switch (q->type)
195    {
196    case MR_Q_RETRIEVE:
197      /* for queries that do not permit wildcarding, check if row
198         uniquely exists */
199      if (v && v->field)
200        {
201          status = validate_row(q, Argv, v);
202          if (status != MR_EXISTS)
203            break;
204        }
205
206      /* build "where" clause if needed */
207      if (q->qual)
208        qual = build_qual(q->qual, q->argc, Argv);
209
210      /* if there is a followup routine, then we must save the results */
211      /* of the first query for use by the followup routine */
212      /* if q->rvar = NULL, perform post_rtn only */
213      if (q->rvar)
214        {
215          if (v && v->post_rtn)
216            {
217              sq = sq_create();
218              status = do_retrieve(q, qual, sq_save_args, sq);
219              if (status != MR_SUCCESS)
220                {
221                  char **argv;
222                  int i;
223
224                  while (sq_get_data(sq, &argv))
225                    {
226                      for (i = 0; i < q->vcnt; i++)
227                        free(argv[i]);
228                      free(argv);
229                    }
230                  sq_destroy(sq);
231                  break;
232                }
233              status = (*v->post_rtn)(q, sq, v, action, actarg, cl);
234            }
235          else
236            {
237              /* normal retrieve */
238              status = do_retrieve(q, qual, action, actarg);
239            }
240          if (status != MR_SUCCESS)
241            break;
242        }
243      else
244        status = (*v->post_rtn)(q, Argv, cl, action, actarg);
245
246      break;
247
248    case MR_Q_UPDATE:
249      /* see if row already exists */
250      if (v->field)
251        {
252          status = validate_row(q, Argv, v);
253          if (status != MR_EXISTS)
254            break;
255        }
256
257      /* build "where" clause and perform update */
258      /* if q->rvar = NULL, perform post_rtn only */
259      if (q->rvar)
260        {
261          qual = build_qual(q->qual, q->argc, Argv);
262          incremental_before(q->rtable, qual, argv_ro);
263          status = do_update(q, &Argv[q->argc], qual, action, actarg);
264          incremental_after(q->rtable, qual, argv_ro);
265          if (status != MR_SUCCESS)
266            break;
267          table = table_name[q->rtable];
268          if (strcmp(q->shortname, "sshi") && strcmp(q->shortname, "ssif"))
269            {
270              EXEC SQL UPDATE tblstats
271                SET updates = updates + 1, modtime = SYSDATE
272                WHERE table_name = :table;
273            }
274        }
275
276      /* execute followup routine (if any) */
277      if (v->post_rtn)
278        status = (*v->post_rtn)(q, Argv, cl);
279
280      break;
281
282    case MR_Q_APPEND:
283      /* see if row already exists */
284      if (v->field)
285        {
286          status = validate_row(q, Argv, v);
287          if (status != MR_NO_MATCH)
288            break;
289        }
290
291      /* build "where" clause if needed */
292      if (q->qual)
293        qual = build_qual(q->qual, q->argc, Argv);
294
295      /* perform the append */
296      /* if q->rvar = NULL, perform post_rtn only */
297      if (q->rvar)
298        {
299          incremental_clear_before();
300          status = do_append(q, &Argv[q->argc], qual, action, actarg);
301          if (status != MR_SUCCESS)
302            break;
303          if (v && v->object_id)
304            {
305              qual = realloc(qual, 15 + strlen(q->rvar) +
306                             strlen(Argv[q->argc + q->vcnt]));
307              sprintf(qual, "%s.%s = %s", q->rvar, v->object_id,
308                      Argv[q->argc + q->vcnt]);
309              incremental_after(q->rtable, qual, argv_ro);
310            }
311          else
312            incremental_after(q->rtable, qual, argv_ro);
313
314          table = table_name[q->rtable];
315          EXEC SQL UPDATE tblstats
316            SET appends = appends + 1, modtime = SYSDATE
317            WHERE table_name = :table;
318        }
319
320      /* execute followup routine */
321      if (v->post_rtn)
322        status = (*v->post_rtn)(q, Argv, cl);
323      break;
324
325    case MR_Q_DELETE:
326      /* see if row already exists */
327      if (v->field)
328        {
329          status = validate_row(q, Argv, v);
330          if (status != MR_EXISTS)
331            break;
332        }
333
334      /* build "where" clause and perform delete */
335      /* if q->rvar = NULL, perform post_rtn only */
336      if (q->rvar)
337        {
338          qual = build_qual(q->qual, q->argc, Argv);
339          table = table_name[q->rtable];
340          incremental_before(q->rtable, qual, argv_ro);
341          status = do_delete(q, qual, action, actarg);
342          incremental_clear_after();
343          if (status != MR_SUCCESS)
344            break;
345          EXEC SQL UPDATE tblstats
346            SET deletes = deletes + 1, modtime = SYSDATE
347            WHERE table_name = :table;
348        }
349
350      /* execute followup routine */
351      if (v->post_rtn)
352        status = (*v->post_rtn)(q, Argv, cl);
353      break;
354
355    case MR_Q_SPECIAL:
356      break;
357    }
358
359out:
360  free(qual);
361
362  if (status == MR_SUCCESS && dbms_errno != 0)
363    {
364      com_err(whoami, MR_INTERNAL, "Server didn't notice DBMS ERROR %d",
365              dbms_errno);
366      status = mr_errcode;
367    }
368
369  if (q->type == MR_Q_RETRIEVE)
370    EXEC SQL COMMIT WORK;
371  else
372    {
373      if (status == MR_SUCCESS)
374        {
375          EXEC SQL COMMIT WORK;
376          if (journal)
377            {
378              char *buf;
379              int i;
380              extern time_t now;
381
382              fprintf(journal, "%% %s %s %s",
383                      cl->clname, cl->entity, ctime(&now));
384              fprintf(journal, "%s ", q->name);
385              for (i = 0; i < argc; i++)
386                {
387                  if (i != 0)
388                    putc(' ', journal);
389                  buf = requote(argv_ro[i]);
390                  fputs(buf, journal);
391                  free(buf);
392                }
393              putc('\n', journal);
394              fflush(journal);
395            }
396          incremental_update();
397        }
398      else
399        {
400          EXEC SQL ROLLBACK WORK;
401          incremental_flush();
402        }
403    }
404
405  if (status != MR_SUCCESS)
406    com_err(whoami, status, " (Query failed)");
407  return status;
408}
409
410char *build_qual(char *fmt_buf, int argc, char *argv[])
411{
412  char *res, *result_buf, *fmt, *arg, *like, *p;
413
414  result_buf = xmalloc(2 * (strlen(fmt_buf) + argc * ARGLEN));
415
416  res = result_buf;
417  fmt = fmt_buf;
418
419  like = strstr(fmt, "LIKE");
420  arg = strchr(fmt, '%');
421
422  /* Look through the format for LIKE expressions and arguments.
423     Substitute in the arguments, simplify the `LIKE's to `='s
424     where possible, and insert ESCAPE clauses where needed */
425
426  while (*fmt)
427    {
428     
429      if ((!like && !arg) || argc == 0)
430        {
431          /* only plain text remains */
432          strcpy(res, fmt);
433          res = strchr(res, '\0');
434          break;
435        }
436      else if (!like || arg < like)
437        {
438          /* regular arg: copy up to arg, then substitute */
439          strncpy(res, fmt, arg - fmt);
440          res += arg - fmt;
441          if (*++arg)
442            {
443              switch (*arg++)
444                {
445                case '%':
446                  *res++ = '%';
447                  break;
448
449                case 's':
450                  p = *argv;
451                  /* copy string, doubling single quotes */
452                  while (*p)
453                    {
454                      if (*p == '\'')
455                        *res++ = '\'';
456                      *res++ = *p++;
457                    }
458                  argv++;
459                  break;
460
461                case 'd':
462                  res += sprintf(res, "%d", *(int *)*argv++);
463                  break;
464                }
465            }
466          fmt = arg;
467          arg = strchr(fmt, '%');
468        } else {
469          /* LIKE arg: copy over up to the arg, then copy and convert arg */
470          int escape = 0, pattern = 0;
471          char *likepos = res + (like - fmt);
472
473          strncpy(res, fmt, arg - fmt);
474          res += arg - fmt;
475
476          /* copy arg, converting UNIX globs to `SQL voodoo', and noting
477             if we'll need an ESCAPE clause */
478          for (p = *argv++; *p; p++)
479            {
480              switch (*p)
481                {
482                case '*':
483                  *res++ = '%';
484                  *res++ = '%'; /* need to double for build_sql_stmt */
485                  pattern = 1;
486                  break;
487
488                case '?':
489                  *res++ = '_';
490                  pattern = 1;
491                  break;
492
493                case '%':
494                case '_':
495                  *res++ = '*';
496                  *res++ = *p;
497                  if (*p == '%')
498                    *res++ = *p;
499                  escape = 1;
500                  break;
501
502                case '\'':
503                  *res++ = '\'';
504                  /* fall through */
505
506                default:
507                  *res++ = *p;
508                }
509            }
510
511          /* if no pattern characters, write over "LIKE" with " =  " */
512          if (!pattern && !escape)
513            memcpy(likepos, " =  ", 4);
514
515          fmt = arg + 2;
516          while (*fmt && *fmt != ' ')
517            *res++ = *fmt++;
518
519          if (escape)
520            res += sprintf(res, " ESCAPE '*'");
521
522          arg = strchr(fmt, '%');
523          like = strstr(fmt, "LIKE");
524        }
525    }
526
527  *res = '\0';
528  result_buf = realloc(result_buf, strlen(result_buf) + 1);
529  return result_buf;
530}
531
532/* Build arguement vector, verify query and arguments */
533
534int privileged;
535
536int mr_verify_query(client *cl, struct query *q, int argc, char *argv_ro[])
537{
538  int argreq;
539  int status;
540  struct validate *v = q->validate;
541  int i;
542  char *to, *fr, *stop;
543
544  privileged = 0;
545
546  /* check argument count */
547  argreq = q->argc;
548  if (q->type == MR_Q_UPDATE || q->type == MR_Q_APPEND)
549    argreq += q->vcnt;
550  if (argc != argreq)
551    return MR_ARGS;
552
553  /* copy the arguments into a local argv that we can modify */
554  for (i = 0; i < argc; i++)
555    {
556      for (to = Argv[i], fr = argv_ro[i], stop = to + MAX_FIELD_WIDTH; (*fr) && (to < stop);)
557        *to++ = *fr++;
558
559      if (*fr)
560        return MR_ARG_TOO_LONG;
561      *to = '\0';
562    }
563
564  /* Check initial query access.  If we're acting as a proxy, only allow
565   * access if the query has "default" as a capacl.
566   */
567  status = check_query_access(q, Argv, cl);
568  if (status != MR_SUCCESS && status != MR_PERM)
569    return status;
570  if (status == MR_SUCCESS && (!cl->proxy_id || q->everybody))
571      privileged++;
572
573  /* validate arguments */
574  if (v && v->valobj)
575    {
576      status = validate_fields(q, Argv, v->valobj, v->objcnt);
577      if (status != MR_SUCCESS)
578        return status;
579    }
580
581  /* perform special query access check */
582  if (!privileged && v && v->acs_rtn)
583    {
584      status = (*v->acs_rtn)(q, Argv, cl);
585      if (status != MR_SUCCESS && status != MR_PERM)
586        return status;
587      if (status == MR_SUCCESS)
588        return MR_SUCCESS;
589    }
590
591  return privileged ? MR_SUCCESS : MR_PERM;
592}
593
594int check_query_access(struct query *q, char *argv[], client *cl)
595{
596  EXEC SQL BEGIN DECLARE SECTION;
597  char *name;
598  int acl_id;
599  static int def_uid;
600  EXEC SQL END DECLARE SECTION;
601
602  /* initialize default uid */
603  if (def_uid == 0)
604    EXEC SQL SELECT users_id INTO :def_uid FROM users WHERE login = 'default';
605
606  name = q->shortname;
607  EXEC SQL SELECT list_id INTO :acl_id FROM capacls WHERE tag = :name;
608  if (sqlca.sqlcode < 0)
609    return MR_DBMS_ERR;
610  if (sqlca.sqlcode == SQL_NO_MATCH)
611    return MR_PERM;
612  q->acl = acl_id;
613 
614  /* check for default access */
615  EXEC SQL SELECT member_id INTO :acl_id FROM imembers
616    WHERE list_id = :acl_id AND member_type = 'USER'
617    AND member_id = :def_uid;
618  if (sqlca.sqlerrd[2] == 0)
619    q->everybody = 0;
620  else
621    q->everybody = 1;
622
623  if (q->everybody)
624    return MR_SUCCESS;
625
626  if (find_member("LIST", acl_id, cl))
627    return MR_SUCCESS;
628  else
629    return MR_PERM;
630}
631
632int find_member(char *list_type, int list_id, client *cl)
633{
634  EXEC SQL BEGIN DECLARE SECTION;
635  int flag, users_id, client_id;
636  EXEC SQL END DECLARE SECTION;
637
638  if (!strcmp(strtrim(list_type), "USER") && list_id == cl->users_id)
639    return 1;
640
641  if (!strcmp(strtrim(list_type), "KERBEROS") && list_id == -cl->client_id)
642    return 1;
643
644  /* see if client is a member of list */
645  flag = 0;
646  users_id = cl->users_id;
647  client_id = -cl->client_id;
648  EXEC SQL SELECT COUNT(member_id) INTO :flag FROM imembers
649    WHERE list_id = :list_id
650    AND ( ( member_type = 'USER' AND member_id = :users_id )
651          OR (member_type = 'KERBEROS' AND member_id = :client_id ) );
652  if (sqlca.sqlcode == 0)
653    return flag;
654  return 0;
655}
656
657
658int do_retrieve(struct query *q, char *pqual,
659                int (*action)(int, char *[], void *), void *actarg)
660{
661  build_sql_stmt(stmt_buf, "SELECT", q->tlist, NULL, pqual);
662  if (q->sort)
663    {
664      strcat(stmt_buf, " ORDER BY ");
665      strcat(stmt_buf, q->sort);
666    }
667
668  return do_for_all_rows(stmt_buf, q->vcnt, action, actarg);
669}
670
671void build_sql_stmt(char *result_buf, char *cmd, char *targetlist,
672                    char *argv[], char *qual)
673{
674  char fmt_buf[MR_STMTBUF_LEN];
675  char *res, *fmt;
676
677  if (qual)
678    sprintf(fmt_buf, "%s %s WHERE %s", cmd, targetlist, qual);
679  else
680    sprintf(fmt_buf, "%s %s", cmd, targetlist);
681
682  for (res = result_buf, fmt = fmt_buf; *fmt; fmt++)
683    {
684      if (*fmt == '%')
685        {
686          if (*++fmt)
687            {
688              switch (*fmt)
689                {
690                case '%':                       /* %% -> % */
691                  *res++ = *fmt;
692                  break;
693                case 's':
694                  if (*argv[0])
695                    {
696                      char *p = *argv;
697                      while (*p)
698                        {
699                          if (*p == '\'')
700                            *res++ = '\'';      /* double the ' */
701                          *res++ = *p++;
702                        }
703                    }
704                  argv++;
705                  break;
706                case 'd':
707                  res += sprintf(res, "%d", *(int *)*argv++);
708                  break;
709                default:                        /* Swallow other %? pairs */
710                  break;
711                }
712            }
713          else
714            break;
715        }
716      else
717        *res++ = *fmt;                          /* text -> result buffer */
718    }
719  *res = '\0';
720}
721
722int do_update(struct query *q, char *argv[], char *qual,
723              int (*action)(int, char *[], void *), void *actarg)
724{
725  build_sql_stmt(stmt_buf, "UPDATE", q->tlist, argv, qual);
726  EXEC SQL EXECUTE IMMEDIATE :stmt_buf;
727  if (mr_errcode)
728    return mr_errcode;
729  return MR_SUCCESS;
730}
731
732int do_append(struct query *q, char *argv[], char *pqual,
733              int (*action)(int, char *[], void *), void *actarg)
734{
735  build_sql_stmt(stmt_buf, "INSERT", q->tlist, argv, pqual);
736  EXEC SQL EXECUTE IMMEDIATE :stmt_buf;
737  if (mr_errcode)
738    return mr_errcode;
739  return MR_SUCCESS;
740}
741
742int do_delete(struct query *q, char *qual,
743              int (*action)(int, char *[], void *), void *actarg)
744{
745  sprintf(stmt_buf, "DELETE FROM %s WHERE %s", table_name[q->rtable], qual);
746  EXEC SQL EXECUTE IMMEDIATE :stmt_buf;
747  if (mr_errcode)
748    return mr_errcode;
749  return MR_SUCCESS;
750}
751
752
753/**
754 ** set_next_object_id - set next object id in values table
755 **
756 ** Inputs: object - object name in values table and in objects
757 **         table - name of table objects are found in
758 **         limit - should the ID be range limited
759 **
760 ** - called before an MR_Q_APPEND operation to set the next object id to
761 **   be used for the new record to the next free value
762 **
763 **/
764
765int set_next_object_id(char *object, enum tables table, int limit)
766{
767  EXEC SQL BEGIN DECLARE SECTION;
768  int value;
769  char *obj = object;
770  EXEC SQL END DECLARE SECTION;
771  int starting_value;
772
773  EXEC SQL SELECT value INTO :value FROM numvalues WHERE name = :obj;
774  if (sqlca.sqlerrd[2] != 1)
775    return MR_NO_ID;
776
777  starting_value = value;
778  while (1)
779    {
780#ifdef ULTRIX_ID_HOLE
781      if (limit && value > 31999 && value < 32768)
782        value = 32768;
783#endif
784      if (limit && value > MAX_ID_VALUE)
785        value = MIN_ID_VALUE;
786
787      sprintf(stmt_buf, "SELECT %s FROM %s WHERE %s = %d",
788              object, table_name[table], object, value);
789      dosql(sqlbuffer);
790      if (sqlca.sqlcode < 0)
791        return mr_errcode;
792      if (sqlca.sqlcode == SQL_NO_MATCH)
793        break;
794
795      value++;
796      if (limit && value == starting_value)
797        {
798          com_err(whoami, 0, "All id values have been used");
799          return MR_NO_ID;
800        }
801    }
802
803  com_err(whoami, 0, "setting ID %s to %d", object, value);
804  EXEC SQL UPDATE numvalues SET value = :value WHERE name = :obj;
805  return MR_SUCCESS;
806}
807
808
809/* Turn a kerberos name into the user's ID of the account that principal
810 * owns.  Sets the kerberos ID and user ID.
811 */
812
813int set_krb_mapping(char *name, char *login, int ok, int *kid, int *uid)
814{
815  EXEC SQL BEGIN DECLARE SECTION;
816  int u_id, k_id;
817  char *krbname;
818  EXEC SQL END DECLARE SECTION;
819
820  krbname = name;
821  *kid = 0;
822  *uid = 0;
823
824  EXEC SQL SELECT km.users_id, km.string_id INTO :u_id, :k_id
825    FROM krbmap km, strings str
826    WHERE km.string_id = str.string_id AND str.string = :krbname;
827  EXEC SQL COMMIT WORK;
828
829  if (dbms_errno)
830    return mr_errcode;
831
832  if (sqlca.sqlerrd[2] == 1)
833    {
834      *kid = -k_id;
835      *uid = u_id;
836      return MR_SUCCESS;
837    }
838
839  if (name_to_id(name, STRINGS_TABLE, &k_id) == MR_SUCCESS)
840    *kid = -k_id;
841
842  if (!ok)
843    {
844      *uid = *kid;
845      return MR_SUCCESS;
846    }
847
848  if (name_to_id(login, USERS_TABLE, uid) != MR_SUCCESS)
849    *uid = 0;
850
851  if (*kid == 0)
852    *kid = *uid;
853  if (dbms_errno)
854    return mr_errcode;
855  return MR_SUCCESS;
856}
857
858
859void sanity_check_queries(void)
860{
861  int i;
862  int maxv = 0, maxa = 0;
863
864#define MAX(x, y) ((x) > (y) ? (x) : (y))
865
866  for (i = 0; i < QueryCount; i++)
867    {
868      maxv = MAX(maxv, Queries[i].vcnt);
869      maxa = MAX(maxa, Queries[i].argc);
870      max_version = MAX(max_version, Queries[i].version);
871    }
872  if (MAX(maxv, maxa) > QMAXARGS)
873    {
874      com_err(whoami, 0, "A query has more args than QMAXARGS");
875      exit(1);
876    }
877}
878
879
880/* Generically do a SELECT, storing the results in the provided buffers */
881
882void dosql(char *buffers[])
883{
884  int i, errcode = 0, errlen;
885
886  EXEC SQL PREPARE inc_stmt FROM :stmt_buf;
887  if (sqlca.sqlcode)
888    return;
889  EXEC SQL DECLARE inc_crs CURSOR FOR inc_stmt;
890  EXEC SQL OPEN inc_crs;
891  mr_sqlda->N = QMAXARGS;
892  EXEC SQL DESCRIBE SELECT LIST FOR inc_stmt INTO mr_sqlda;
893  mr_sqlda->N = mr_sqlda->F;
894  for (i = 0; i < mr_sqlda->N; i++)
895    {
896      mr_sqlda->V[i] = buffers[i];
897      mr_sqlda->T[i] = 97;
898      mr_sqlda->L[i] = MAX_FIELD_WIDTH;
899    }
900  EXEC SQL FETCH inc_crs USING DESCRIPTOR mr_sqlda;
901
902  /* if we got an error from the FETCH, we have to preserve it or the
903     close will reset it and the caller will think nothing happened */
904  if (sqlca.sqlcode)
905    {
906      errcode = sqlca.sqlcode;
907      errlen = sqlca.sqlerrm.sqlerrml;
908    }
909
910  EXEC SQL CLOSE inc_crs;
911  if (errcode)
912    {
913      sqlca.sqlcode = errcode;
914      sqlca.sqlerrm.sqlerrml = errlen;
915    }
916}
917
918int do_for_all_rows(char *query, int count,
919                    int (*action)(int, char *[], void *), void *actarg)
920{
921  int i, rowcount = 0;
922  EXEC SQL BEGIN DECLARE SECTION;
923  char *q = query;
924  EXEC SQL END DECLARE SECTION;
925
926  EXEC SQL PREPARE stmt FROM :q;
927  if (sqlca.sqlcode)
928    return MR_INTERNAL;
929  EXEC SQL DECLARE curs CURSOR FOR stmt;
930  EXEC SQL OPEN curs;
931  mr_sqlda->N = count;
932  EXEC SQL DESCRIBE SELECT LIST FOR stmt INTO mr_sqlda;
933  mr_sqlda->N = mr_sqlda->F;
934  for (i = 0; i < mr_sqlda->N; i++)
935    {
936      mr_sqlda->V[i] = sqlbuffer[i];
937      mr_sqlda->T[i] = 97;
938      mr_sqlda->L[i] = MAX_FIELD_WIDTH;
939    }
940
941  while (rowcount < max_row_count)
942    {
943      EXEC SQL FETCH curs USING DESCRIPTOR mr_sqlda;
944      if (sqlca.sqlcode)
945        break;
946      (*action)(count, sqlbuffer, actarg);
947      rowcount++;
948    }
949  EXEC SQL CLOSE curs;
950
951  if (mr_errcode)
952    return mr_errcode;
953  if (rowcount == max_row_count)
954    {
955      critical_alert(whoami, "moirad", "attempted query with too many rows");
956      return MR_NO_MEM;
957    }
958  else if (rowcount == 0)
959    return MR_NO_MATCH;
960  else
961    return MR_SUCCESS;
962}
963
964/* Do a name to ID translation.  Moved from cache.pc because we did away
965 * with the cache, but what this function does is still useful to us.
966 */
967
968int name_to_id(char *name, enum tables type, int *id)
969{
970  EXEC SQL BEGIN DECLARE SECTION;
971  char *iname;
972  int j;
973  EXEC SQL END DECLARE SECTION;
974
975  iname = name;
976
977  switch (type)
978    {
979    case USERS_TABLE:
980      if (strchr(iname, '@') || (strlen(iname) > 8))
981        {
982          sqlca.sqlcode = SQL_NO_MATCH;
983          break;
984        }
985      EXEC SQL SELECT users_id INTO :j FROM users WHERE login = :iname;
986      break;
987    case LIST_TABLE:
988      EXEC SQL SELECT list_id INTO :j FROM list WHERE name = :iname;
989      break;
990    case MACHINE_TABLE:
991      EXEC SQL SELECT mach_id INTO :j FROM machine WHERE name = UPPER(:iname);
992      break;
993    case SUBNET_TABLE:
994      EXEC SQL SELECT snet_id INTO :j FROM subnet WHERE name = UPPER(:iname);
995      break;
996    case CLUSTERS_TABLE:
997      EXEC SQL SELECT clu_id INTO :j FROM clusters WHERE name = :iname;
998      break;
999    case FILESYS_TABLE:
1000      EXEC SQL SELECT filsys_id INTO :j FROM filesys WHERE label = :iname;
1001      break;
1002    case STRINGS_TABLE:
1003      if (!iname[0])    /* special-case empty string */
1004        {
1005          *id = 0;
1006          return MR_SUCCESS;
1007        }
1008      EXEC SQL SELECT string_id INTO :j FROM strings WHERE string = :iname;
1009      break;
1010    case CONTAINERS_TABLE:
1011      EXEC SQL SELECT cnt_id INTO :j FROM containers WHERE LOWER(name) =
1012        LOWER(:iname);
1013      break;
1014    default:
1015      return MR_INTERNAL;
1016    }
1017  if (sqlca.sqlcode == SQL_NO_MATCH)
1018    return MR_NO_MATCH;
1019  if (sqlca.sqlerrd[2] > 1)
1020    return MR_NOT_UNIQUE;
1021  if (sqlca.sqlcode)
1022    return MR_DBMS_ERR;
1023  *id = j;
1024
1025  return MR_SUCCESS;
1026}
1027
1028/* Perform an ID to name mapping.  name should be a pointer to a pointer to
1029 * malloc'ed data.  The buffer it refers to will be freed, and a new buffer
1030 * allocated with the answer.
1031 *
1032 * This used to be in cache.pc, but we've removed the cache, and this function
1033 * is still useful to us.
1034 */
1035
1036int id_to_name(int id, enum tables type, char **name)
1037{
1038  EXEC SQL BEGIN DECLARE SECTION;
1039  char iname[MAX_FIELD_WIDTH];
1040  int j;
1041  EXEC SQL END DECLARE SECTION;
1042
1043  j = id;
1044
1045  switch (type)
1046    {
1047    case USERS_TABLE:
1048      EXEC SQL SELECT login INTO :iname FROM users WHERE users_id = :j;
1049      break;
1050    case LIST_TABLE:
1051      EXEC SQL SELECT name INTO :iname FROM list WHERE list_id = :j;
1052      break;
1053    case MACHINE_TABLE:
1054      EXEC SQL SELECT name INTO :iname FROM machine WHERE mach_id = :j;
1055      break;
1056    case SUBNET_TABLE:
1057      EXEC SQL SELECT name INTO :iname FROM subnet WHERE snet_id = :j;
1058      break;
1059    case CLUSTERS_TABLE:
1060      EXEC SQL SELECT name INTO :iname FROM clusters WHERE clu_id = :j;
1061      break;
1062    case FILESYS_TABLE:
1063      EXEC SQL SELECT label INTO :iname FROM filesys WHERE filsys_id = :j;
1064      break;
1065    case STRINGS_TABLE:
1066      EXEC SQL SELECT string INTO :iname FROM strings WHERE string_id = :j;
1067      break;
1068    case CONTAINERS_TABLE:
1069      EXEC SQL SELECT name INTO :iname FROM containers WHERE cnt_id = :j;
1070      break;
1071    default:
1072      return MR_INTERNAL;
1073    }
1074  if (sqlca.sqlcode == SQL_NO_MATCH)
1075    {
1076      free(*name);
1077      sprintf(iname, "#%d", j);
1078      *name = xstrdup(iname);
1079      return MR_NO_MATCH;
1080    }
1081  if (sqlca.sqlerrd[2] > 1)
1082    return MR_INTERNAL;
1083  if (sqlca.sqlcode)
1084    return MR_DBMS_ERR;
1085  free(*name);
1086  *name = xstrdup(strtrim(iname));
1087
1088  return MR_SUCCESS;
1089}
1090
1091/* eof:qrtn.dc */
Note: See TracBrowser for help on using the repository browser.