1 | /* $Id: qrtn.pc,v 2.31 2009-07-06 16:23:15 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 | |
---|
20 | EXEC SQL INCLUDE sqlca; /* SQL Communications Area */ |
---|
21 | EXEC SQL INCLUDE sqlda; /* SQL Descriptor Area */ |
---|
22 | |
---|
23 | RCSID("$Header: /afs/athena.mit.edu/astaff/project/moiradev/repository/moira/server/qrtn.pc,v 2.31 2009-07-06 16:23:15 zacheiss Exp $"); |
---|
24 | |
---|
25 | SQLDA *mr_sqlda; |
---|
26 | EXEC SQL BEGIN DECLARE SECTION; |
---|
27 | char stmt_buf[MR_STMTBUF_LEN]; |
---|
28 | int proxy_acl; |
---|
29 | EXEC SQL END DECLARE SECTION; |
---|
30 | |
---|
31 | char *Argv[QMAXARGS]; |
---|
32 | extern char *table_name[]; |
---|
33 | extern char *sqlbuffer[QMAXARGS]; |
---|
34 | |
---|
35 | int dbms_errno = 0; |
---|
36 | int mr_errcode = 0; |
---|
37 | EXEC SQL BEGIN DECLARE SECTION; |
---|
38 | int query_timeout = 30; |
---|
39 | char *database = "moira"; |
---|
40 | EXEC SQL END DECLARE SECTION; |
---|
41 | extern char *whoami; |
---|
42 | extern FILE *journal; |
---|
43 | extern int QueryCount, max_version; |
---|
44 | extern struct query Queries[]; |
---|
45 | |
---|
46 | /* Put this in a variable so that we can patch it if necessary */ |
---|
47 | int max_row_count = 8192; |
---|
48 | |
---|
49 | int mr_verify_query(client *cl, struct query *q, int argc, char *argv_ro[]); |
---|
50 | int do_retrieve(struct query *q, char *pqual, |
---|
51 | int (*action)(int, char *[], void *), void *actarg); |
---|
52 | int do_update(struct query *q, char *argv[], char *qual, |
---|
53 | int (*action)(int, char *[], void *), void *actarg); |
---|
54 | int do_append(struct query *q, char *argv[], char *pqual, |
---|
55 | int (*action)(int, char *[], void *), void *actarg); |
---|
56 | int do_delete(struct query *q, char *qual, |
---|
57 | int (*action)(int, char *[], void *), void *actarg); |
---|
58 | void build_sql_stmt(char *result_buf, char *cmd, char *targetlist, |
---|
59 | char *argv[], char *qual); |
---|
60 | |
---|
61 | SQLDA *mr_alloc_sqlda(void); |
---|
62 | void sqlglm(char *, int *, int *); |
---|
63 | |
---|
64 | /* |
---|
65 | * dbmserr: Called when the DBMS indicates an error. |
---|
66 | */ |
---|
67 | |
---|
68 | void 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. */ |
---|
86 | EXEC SQL WHENEVER SQLERROR DO dbmserr(); |
---|
87 | |
---|
88 | int 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 | |
---|
123 | void mr_close_database(void) |
---|
124 | { |
---|
125 | EXEC SQL COMMIT RELEASE; |
---|
126 | } |
---|
127 | |
---|
128 | int 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 | |
---|
142 | int 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 | |
---|
359 | out: |
---|
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 | |
---|
410 | char *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 | |
---|
534 | int privileged; |
---|
535 | |
---|
536 | int 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 | |
---|
594 | int 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 | |
---|
632 | int 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 | if (!strcmp(strtrim(list_type), "LIST")) |
---|
645 | { |
---|
646 | /* see if client is a member of list */ |
---|
647 | flag = 0; |
---|
648 | users_id = cl->users_id; |
---|
649 | client_id = -cl->client_id; |
---|
650 | EXEC SQL SELECT COUNT(member_id) INTO :flag FROM imembers |
---|
651 | WHERE list_id = :list_id |
---|
652 | AND ( ( member_type = 'USER' AND member_id = :users_id ) |
---|
653 | OR (member_type = 'KERBEROS' AND member_id = :client_id ) ); |
---|
654 | if (sqlca.sqlcode == 0) |
---|
655 | return flag; |
---|
656 | } |
---|
657 | |
---|
658 | return 0; |
---|
659 | } |
---|
660 | |
---|
661 | |
---|
662 | int do_retrieve(struct query *q, char *pqual, |
---|
663 | int (*action)(int, char *[], void *), void *actarg) |
---|
664 | { |
---|
665 | build_sql_stmt(stmt_buf, "SELECT", q->tlist, NULL, pqual); |
---|
666 | if (q->sort) |
---|
667 | { |
---|
668 | strcat(stmt_buf, " ORDER BY "); |
---|
669 | strcat(stmt_buf, q->sort); |
---|
670 | } |
---|
671 | |
---|
672 | return do_for_all_rows(stmt_buf, q->vcnt, action, actarg); |
---|
673 | } |
---|
674 | |
---|
675 | void build_sql_stmt(char *result_buf, char *cmd, char *targetlist, |
---|
676 | char *argv[], char *qual) |
---|
677 | { |
---|
678 | char fmt_buf[MR_STMTBUF_LEN]; |
---|
679 | char *res, *fmt; |
---|
680 | |
---|
681 | if (qual) |
---|
682 | sprintf(fmt_buf, "%s %s WHERE %s", cmd, targetlist, qual); |
---|
683 | else |
---|
684 | sprintf(fmt_buf, "%s %s", cmd, targetlist); |
---|
685 | |
---|
686 | for (res = result_buf, fmt = fmt_buf; *fmt; fmt++) |
---|
687 | { |
---|
688 | if (*fmt == '%') |
---|
689 | { |
---|
690 | if (*++fmt) |
---|
691 | { |
---|
692 | switch (*fmt) |
---|
693 | { |
---|
694 | case '%': /* %% -> % */ |
---|
695 | *res++ = *fmt; |
---|
696 | break; |
---|
697 | case 's': |
---|
698 | if (*argv[0]) |
---|
699 | { |
---|
700 | char *p = *argv; |
---|
701 | while (*p) |
---|
702 | { |
---|
703 | if (*p == '\'') |
---|
704 | *res++ = '\''; /* double the ' */ |
---|
705 | *res++ = *p++; |
---|
706 | } |
---|
707 | } |
---|
708 | argv++; |
---|
709 | break; |
---|
710 | case 'd': |
---|
711 | res += sprintf(res, "%d", *(int *)*argv++); |
---|
712 | break; |
---|
713 | default: /* Swallow other %? pairs */ |
---|
714 | break; |
---|
715 | } |
---|
716 | } |
---|
717 | else |
---|
718 | break; |
---|
719 | } |
---|
720 | else |
---|
721 | *res++ = *fmt; /* text -> result buffer */ |
---|
722 | } |
---|
723 | *res = '\0'; |
---|
724 | } |
---|
725 | |
---|
726 | int do_update(struct query *q, char *argv[], char *qual, |
---|
727 | int (*action)(int, char *[], void *), void *actarg) |
---|
728 | { |
---|
729 | build_sql_stmt(stmt_buf, "UPDATE", q->tlist, argv, qual); |
---|
730 | EXEC SQL EXECUTE IMMEDIATE :stmt_buf; |
---|
731 | if (mr_errcode) |
---|
732 | return mr_errcode; |
---|
733 | return MR_SUCCESS; |
---|
734 | } |
---|
735 | |
---|
736 | int do_append(struct query *q, char *argv[], char *pqual, |
---|
737 | int (*action)(int, char *[], void *), void *actarg) |
---|
738 | { |
---|
739 | build_sql_stmt(stmt_buf, "INSERT", q->tlist, argv, pqual); |
---|
740 | EXEC SQL EXECUTE IMMEDIATE :stmt_buf; |
---|
741 | if (mr_errcode) |
---|
742 | return mr_errcode; |
---|
743 | return MR_SUCCESS; |
---|
744 | } |
---|
745 | |
---|
746 | int do_delete(struct query *q, char *qual, |
---|
747 | int (*action)(int, char *[], void *), void *actarg) |
---|
748 | { |
---|
749 | sprintf(stmt_buf, "DELETE FROM %s WHERE %s", table_name[q->rtable], qual); |
---|
750 | EXEC SQL EXECUTE IMMEDIATE :stmt_buf; |
---|
751 | if (mr_errcode) |
---|
752 | return mr_errcode; |
---|
753 | return MR_SUCCESS; |
---|
754 | } |
---|
755 | |
---|
756 | |
---|
757 | /** |
---|
758 | ** set_next_object_id - set next object id in values table |
---|
759 | ** |
---|
760 | ** Inputs: object - object name in values table and in objects |
---|
761 | ** table - name of table objects are found in |
---|
762 | ** limit - should the ID be range limited |
---|
763 | ** |
---|
764 | ** - called before an MR_Q_APPEND operation to set the next object id to |
---|
765 | ** be used for the new record to the next free value |
---|
766 | ** |
---|
767 | **/ |
---|
768 | |
---|
769 | int set_next_object_id(char *object, enum tables table, int limit) |
---|
770 | { |
---|
771 | EXEC SQL BEGIN DECLARE SECTION; |
---|
772 | int value; |
---|
773 | char *obj = object; |
---|
774 | EXEC SQL END DECLARE SECTION; |
---|
775 | int starting_value; |
---|
776 | |
---|
777 | EXEC SQL SELECT value INTO :value FROM numvalues WHERE name = :obj; |
---|
778 | if (sqlca.sqlerrd[2] != 1) |
---|
779 | return MR_NO_ID; |
---|
780 | |
---|
781 | starting_value = value; |
---|
782 | while (1) |
---|
783 | { |
---|
784 | #ifdef ULTRIX_ID_HOLE |
---|
785 | if (limit && value > 31999 && value < 32768) |
---|
786 | value = 32768; |
---|
787 | #endif |
---|
788 | if (limit && value > MAX_ID_VALUE) |
---|
789 | value = MIN_ID_VALUE; |
---|
790 | |
---|
791 | sprintf(stmt_buf, "SELECT %s FROM %s WHERE %s = %d", |
---|
792 | object, table_name[table], object, value); |
---|
793 | dosql(sqlbuffer); |
---|
794 | if (sqlca.sqlcode < 0) |
---|
795 | return mr_errcode; |
---|
796 | if (sqlca.sqlcode == SQL_NO_MATCH) |
---|
797 | break; |
---|
798 | |
---|
799 | value++; |
---|
800 | if (limit && value == starting_value) |
---|
801 | { |
---|
802 | com_err(whoami, 0, "All id values have been used"); |
---|
803 | return MR_NO_ID; |
---|
804 | } |
---|
805 | } |
---|
806 | |
---|
807 | com_err(whoami, 0, "setting ID %s to %d", object, value); |
---|
808 | EXEC SQL UPDATE numvalues SET value = :value WHERE name = :obj; |
---|
809 | return MR_SUCCESS; |
---|
810 | } |
---|
811 | |
---|
812 | |
---|
813 | /* Turn a kerberos name into the user's ID of the account that principal |
---|
814 | * owns. Sets the kerberos ID and user ID. |
---|
815 | */ |
---|
816 | |
---|
817 | int set_krb_mapping(char *name, char *login, int ok, int *kid, int *uid) |
---|
818 | { |
---|
819 | EXEC SQL BEGIN DECLARE SECTION; |
---|
820 | int u_id, k_id; |
---|
821 | char *krbname; |
---|
822 | EXEC SQL END DECLARE SECTION; |
---|
823 | |
---|
824 | krbname = name; |
---|
825 | *kid = 0; |
---|
826 | *uid = 0; |
---|
827 | |
---|
828 | EXEC SQL SELECT km.users_id, km.string_id INTO :u_id, :k_id |
---|
829 | FROM krbmap km, strings str |
---|
830 | WHERE km.string_id = str.string_id AND str.string = :krbname; |
---|
831 | EXEC SQL COMMIT WORK; |
---|
832 | |
---|
833 | if (dbms_errno) |
---|
834 | return mr_errcode; |
---|
835 | |
---|
836 | if (sqlca.sqlerrd[2] == 1) |
---|
837 | { |
---|
838 | *kid = -k_id; |
---|
839 | *uid = u_id; |
---|
840 | return MR_SUCCESS; |
---|
841 | } |
---|
842 | |
---|
843 | if (name_to_id(name, STRINGS_TABLE, &k_id) == MR_SUCCESS) |
---|
844 | *kid = -k_id; |
---|
845 | |
---|
846 | if (!ok) |
---|
847 | { |
---|
848 | *uid = *kid; |
---|
849 | return MR_SUCCESS; |
---|
850 | } |
---|
851 | |
---|
852 | if (name_to_id(login, USERS_TABLE, uid) != MR_SUCCESS) |
---|
853 | *uid = 0; |
---|
854 | |
---|
855 | if (*kid == 0) |
---|
856 | *kid = *uid; |
---|
857 | if (dbms_errno) |
---|
858 | return mr_errcode; |
---|
859 | return MR_SUCCESS; |
---|
860 | } |
---|
861 | |
---|
862 | |
---|
863 | void sanity_check_queries(void) |
---|
864 | { |
---|
865 | int i; |
---|
866 | int maxv = 0, maxa = 0; |
---|
867 | |
---|
868 | #define MAX(x, y) ((x) > (y) ? (x) : (y)) |
---|
869 | |
---|
870 | for (i = 0; i < QueryCount; i++) |
---|
871 | { |
---|
872 | maxv = MAX(maxv, Queries[i].vcnt); |
---|
873 | maxa = MAX(maxa, Queries[i].argc); |
---|
874 | max_version = MAX(max_version, Queries[i].version); |
---|
875 | } |
---|
876 | if (MAX(maxv, maxa) > QMAXARGS) |
---|
877 | { |
---|
878 | com_err(whoami, 0, "A query has more args than QMAXARGS"); |
---|
879 | exit(1); |
---|
880 | } |
---|
881 | } |
---|
882 | |
---|
883 | |
---|
884 | /* Generically do a SELECT, storing the results in the provided buffers */ |
---|
885 | |
---|
886 | void dosql(char *buffers[]) |
---|
887 | { |
---|
888 | int i, errcode = 0, errlen; |
---|
889 | |
---|
890 | EXEC SQL PREPARE inc_stmt FROM :stmt_buf; |
---|
891 | if (sqlca.sqlcode) |
---|
892 | return; |
---|
893 | EXEC SQL DECLARE inc_crs CURSOR FOR inc_stmt; |
---|
894 | EXEC SQL OPEN inc_crs; |
---|
895 | mr_sqlda->N = QMAXARGS; |
---|
896 | EXEC SQL DESCRIBE SELECT LIST FOR inc_stmt INTO mr_sqlda; |
---|
897 | mr_sqlda->N = mr_sqlda->F; |
---|
898 | for (i = 0; i < mr_sqlda->N; i++) |
---|
899 | { |
---|
900 | mr_sqlda->V[i] = buffers[i]; |
---|
901 | mr_sqlda->T[i] = 97; |
---|
902 | mr_sqlda->L[i] = MAX_FIELD_WIDTH; |
---|
903 | } |
---|
904 | EXEC SQL FETCH inc_crs USING DESCRIPTOR mr_sqlda; |
---|
905 | |
---|
906 | /* if we got an error from the FETCH, we have to preserve it or the |
---|
907 | close will reset it and the caller will think nothing happened */ |
---|
908 | if (sqlca.sqlcode) |
---|
909 | { |
---|
910 | errcode = sqlca.sqlcode; |
---|
911 | errlen = sqlca.sqlerrm.sqlerrml; |
---|
912 | } |
---|
913 | |
---|
914 | EXEC SQL CLOSE inc_crs; |
---|
915 | if (errcode) |
---|
916 | { |
---|
917 | sqlca.sqlcode = errcode; |
---|
918 | sqlca.sqlerrm.sqlerrml = errlen; |
---|
919 | } |
---|
920 | } |
---|
921 | |
---|
922 | int do_for_all_rows(char *query, int count, |
---|
923 | int (*action)(int, char *[], void *), void *actarg) |
---|
924 | { |
---|
925 | int i, rowcount = 0; |
---|
926 | EXEC SQL BEGIN DECLARE SECTION; |
---|
927 | char *q = query; |
---|
928 | EXEC SQL END DECLARE SECTION; |
---|
929 | |
---|
930 | EXEC SQL PREPARE stmt FROM :q; |
---|
931 | if (sqlca.sqlcode) |
---|
932 | return MR_INTERNAL; |
---|
933 | EXEC SQL DECLARE curs CURSOR FOR stmt; |
---|
934 | EXEC SQL OPEN curs; |
---|
935 | mr_sqlda->N = count; |
---|
936 | EXEC SQL DESCRIBE SELECT LIST FOR stmt INTO mr_sqlda; |
---|
937 | mr_sqlda->N = mr_sqlda->F; |
---|
938 | for (i = 0; i < mr_sqlda->N; i++) |
---|
939 | { |
---|
940 | mr_sqlda->V[i] = sqlbuffer[i]; |
---|
941 | mr_sqlda->T[i] = 97; |
---|
942 | mr_sqlda->L[i] = MAX_FIELD_WIDTH; |
---|
943 | } |
---|
944 | |
---|
945 | while (rowcount < max_row_count) |
---|
946 | { |
---|
947 | EXEC SQL FETCH curs USING DESCRIPTOR mr_sqlda; |
---|
948 | if (sqlca.sqlcode) |
---|
949 | break; |
---|
950 | (*action)(count, sqlbuffer, actarg); |
---|
951 | rowcount++; |
---|
952 | } |
---|
953 | EXEC SQL CLOSE curs; |
---|
954 | |
---|
955 | if (mr_errcode) |
---|
956 | return mr_errcode; |
---|
957 | if (rowcount == max_row_count) |
---|
958 | { |
---|
959 | critical_alert(whoami, "moirad", "attempted query with too many rows"); |
---|
960 | return MR_NO_MEM; |
---|
961 | } |
---|
962 | else if (rowcount == 0) |
---|
963 | return MR_NO_MATCH; |
---|
964 | else |
---|
965 | return MR_SUCCESS; |
---|
966 | } |
---|
967 | |
---|
968 | /* Do a name to ID translation. Moved from cache.pc because we did away |
---|
969 | * with the cache, but what this function does is still useful to us. |
---|
970 | */ |
---|
971 | |
---|
972 | int name_to_id(char *name, enum tables type, int *id) |
---|
973 | { |
---|
974 | EXEC SQL BEGIN DECLARE SECTION; |
---|
975 | char *iname; |
---|
976 | int j; |
---|
977 | EXEC SQL END DECLARE SECTION; |
---|
978 | |
---|
979 | iname = name; |
---|
980 | |
---|
981 | switch (type) |
---|
982 | { |
---|
983 | case USERS_TABLE: |
---|
984 | if (strchr(iname, '@') || (strlen(iname) > 8)) |
---|
985 | { |
---|
986 | sqlca.sqlcode = SQL_NO_MATCH; |
---|
987 | break; |
---|
988 | } |
---|
989 | EXEC SQL SELECT users_id INTO :j FROM users WHERE login = :iname; |
---|
990 | break; |
---|
991 | case LIST_TABLE: |
---|
992 | EXEC SQL SELECT list_id INTO :j FROM list WHERE name = :iname; |
---|
993 | break; |
---|
994 | case MACHINE_TABLE: |
---|
995 | EXEC SQL SELECT mach_id INTO :j FROM machine WHERE name = UPPER(:iname); |
---|
996 | break; |
---|
997 | case SUBNET_TABLE: |
---|
998 | EXEC SQL SELECT snet_id INTO :j FROM subnet WHERE name = UPPER(:iname); |
---|
999 | break; |
---|
1000 | case CLUSTERS_TABLE: |
---|
1001 | EXEC SQL SELECT clu_id INTO :j FROM clusters WHERE name = :iname; |
---|
1002 | break; |
---|
1003 | case FILESYS_TABLE: |
---|
1004 | EXEC SQL SELECT filsys_id INTO :j FROM filesys WHERE label = :iname; |
---|
1005 | break; |
---|
1006 | case STRINGS_TABLE: |
---|
1007 | if (!iname[0]) /* special-case empty string */ |
---|
1008 | { |
---|
1009 | *id = 0; |
---|
1010 | return MR_SUCCESS; |
---|
1011 | } |
---|
1012 | EXEC SQL SELECT string_id INTO :j FROM strings WHERE string = :iname; |
---|
1013 | break; |
---|
1014 | case CONTAINERS_TABLE: |
---|
1015 | EXEC SQL SELECT cnt_id INTO :j FROM containers WHERE LOWER(name) = |
---|
1016 | LOWER(:iname); |
---|
1017 | break; |
---|
1018 | default: |
---|
1019 | return MR_INTERNAL; |
---|
1020 | } |
---|
1021 | if (sqlca.sqlcode == SQL_NO_MATCH) |
---|
1022 | return MR_NO_MATCH; |
---|
1023 | if (sqlca.sqlerrd[2] > 1) |
---|
1024 | return MR_NOT_UNIQUE; |
---|
1025 | if (sqlca.sqlcode) |
---|
1026 | return MR_DBMS_ERR; |
---|
1027 | *id = j; |
---|
1028 | |
---|
1029 | return MR_SUCCESS; |
---|
1030 | } |
---|
1031 | |
---|
1032 | /* Perform an ID to name mapping. name should be a pointer to a pointer to |
---|
1033 | * malloc'ed data. The buffer it refers to will be freed, and a new buffer |
---|
1034 | * allocated with the answer. |
---|
1035 | * |
---|
1036 | * This used to be in cache.pc, but we've removed the cache, and this function |
---|
1037 | * is still useful to us. |
---|
1038 | */ |
---|
1039 | |
---|
1040 | int id_to_name(int id, enum tables type, char **name) |
---|
1041 | { |
---|
1042 | EXEC SQL BEGIN DECLARE SECTION; |
---|
1043 | char iname[MAX_FIELD_WIDTH]; |
---|
1044 | int j; |
---|
1045 | EXEC SQL END DECLARE SECTION; |
---|
1046 | |
---|
1047 | j = id; |
---|
1048 | |
---|
1049 | switch (type) |
---|
1050 | { |
---|
1051 | case USERS_TABLE: |
---|
1052 | EXEC SQL SELECT login INTO :iname FROM users WHERE users_id = :j; |
---|
1053 | break; |
---|
1054 | case LIST_TABLE: |
---|
1055 | EXEC SQL SELECT name INTO :iname FROM list WHERE list_id = :j; |
---|
1056 | break; |
---|
1057 | case MACHINE_TABLE: |
---|
1058 | EXEC SQL SELECT name INTO :iname FROM machine WHERE mach_id = :j; |
---|
1059 | break; |
---|
1060 | case SUBNET_TABLE: |
---|
1061 | EXEC SQL SELECT name INTO :iname FROM subnet WHERE snet_id = :j; |
---|
1062 | break; |
---|
1063 | case CLUSTERS_TABLE: |
---|
1064 | EXEC SQL SELECT name INTO :iname FROM clusters WHERE clu_id = :j; |
---|
1065 | break; |
---|
1066 | case FILESYS_TABLE: |
---|
1067 | EXEC SQL SELECT label INTO :iname FROM filesys WHERE filsys_id = :j; |
---|
1068 | break; |
---|
1069 | case STRINGS_TABLE: |
---|
1070 | EXEC SQL SELECT string INTO :iname FROM strings WHERE string_id = :j; |
---|
1071 | break; |
---|
1072 | case CONTAINERS_TABLE: |
---|
1073 | EXEC SQL SELECT name INTO :iname FROM containers WHERE cnt_id = :j; |
---|
1074 | break; |
---|
1075 | default: |
---|
1076 | return MR_INTERNAL; |
---|
1077 | } |
---|
1078 | if (sqlca.sqlcode == SQL_NO_MATCH) |
---|
1079 | { |
---|
1080 | free(*name); |
---|
1081 | sprintf(iname, "#%d", j); |
---|
1082 | *name = xstrdup(iname); |
---|
1083 | return MR_NO_MATCH; |
---|
1084 | } |
---|
1085 | if (sqlca.sqlerrd[2] > 1) |
---|
1086 | return MR_INTERNAL; |
---|
1087 | if (sqlca.sqlcode) |
---|
1088 | return MR_DBMS_ERR; |
---|
1089 | free(*name); |
---|
1090 | *name = xstrdup(strtrim(iname)); |
---|
1091 | |
---|
1092 | return MR_SUCCESS; |
---|
1093 | } |
---|
1094 | |
---|
1095 | /* eof:qrtn.dc */ |
---|