]>
Commit | Line | Data |
---|---|---|
7ac48069 | 1 | /* $Id$ |
73cf66ba | 2 | * |
7ac48069 | 3 | * Query followup 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>. | |
73cf66ba | 8 | * |
9 | */ | |
10 | ||
73cf66ba | 11 | #include <mit-copyright.h> |
73cf66ba | 12 | #include "mr_server.h" |
03c05291 | 13 | #include "query.h" |
7ac48069 | 14 | #include "qrtn.h" |
15 | ||
85330553 | 16 | #include <errno.h> |
73cf66ba | 17 | #include <ctype.h> |
7ac48069 | 18 | #include <stdlib.h> |
03c05291 | 19 | #include <string.h> |
7ac48069 | 20 | |
73cf66ba | 21 | EXEC SQL INCLUDE sqlca; |
7ac48069 | 22 | |
23 | RCSID("$Header$"); | |
73cf66ba | 24 | |
03c05291 | 25 | extern char *whoami, *table_name[]; |
26 | extern int dbms_errno, mr_errcode; | |
73cf66ba | 27 | |
28 | EXEC SQL BEGIN DECLARE SECTION; | |
29 | extern char stmt_buf[]; | |
30 | EXEC SQL END DECLARE SECTION; | |
31 | ||
03c05291 | 32 | EXEC SQL WHENEVER SQLERROR DO dbmserr(); |
73cf66ba | 33 | |
34 | ||
35 | /* FOLLOWUP ROUTINES */ | |
36 | ||
03c05291 | 37 | /* generic set_modtime routine. This takes the table id from the query, |
73cf66ba | 38 | * and will update the modtime, modby, and modwho fields in the entry in |
39 | * the table whose name field matches argv[0]. | |
40 | */ | |
41 | ||
5eaef520 | 42 | int set_modtime(struct query *q, char *argv[], client *cl) |
73cf66ba | 43 | { |
5eaef520 | 44 | char *name, *entity, *table; |
542afef1 | 45 | int who, row = 0; |
73cf66ba | 46 | |
5eaef520 | 47 | entity = cl->entity; |
48 | who = cl->client_id; | |
49 | table = table_name[q->rtable]; | |
542afef1 | 50 | |
51 | if (q->type == MR_Q_UPDATE) | |
52 | row = 1; | |
53 | ||
54 | name = argv[0 + row]; | |
73cf66ba | 55 | |
5eaef520 | 56 | sprintf(stmt_buf, "UPDATE %s SET modtime = SYSDATE, modby = %d, " |
57 | "modwith = '%s' WHERE name = '%s'", table, who, entity, name); | |
58 | EXEC SQL EXECUTE IMMEDIATE :stmt_buf; | |
73cf66ba | 59 | |
5eaef520 | 60 | return MR_SUCCESS; |
73cf66ba | 61 | } |
62 | ||
03c05291 | 63 | /* generic set_modtime_by_id routine. This takes the table id from |
73cf66ba | 64 | * the query, and the id name from the validate record, |
65 | * and will update the modtime, modby, and modwho fields in the entry in | |
66 | * the table whose id matches argv[0]. | |
67 | */ | |
68 | ||
5eaef520 | 69 | int set_modtime_by_id(struct query *q, char *argv[], client *cl) |
73cf66ba | 70 | { |
5eaef520 | 71 | char *entity, *table, *id_name; |
72 | int who, id; | |
73 | ||
74 | entity = cl->entity; | |
75 | who = cl->client_id; | |
76 | table = table_name[q->rtable]; | |
77 | id_name = q->validate->object_id; | |
78 | ||
79 | id = *(int *)argv[0]; | |
80 | sprintf(stmt_buf, "UPDATE %s SET modtime = SYSDATE, modby = %d, " | |
81 | "modwith = '%s' WHERE %s = %d", table, who, entity, id_name, id); | |
82 | EXEC SQL EXECUTE IMMEDIATE :stmt_buf; | |
83 | return MR_SUCCESS; | |
73cf66ba | 84 | } |
85 | ||
86 | ||
87 | /* Sets the finger modtime on a user record. The users_id will be in argv[0]. | |
88 | */ | |
89 | ||
5eaef520 | 90 | int set_finger_modtime(struct query *q, char *argv[], client *cl) |
73cf66ba | 91 | { |
5eaef520 | 92 | EXEC SQL BEGIN DECLARE SECTION; |
93 | int users_id, who; | |
94 | char *entity; | |
95 | EXEC SQL END DECLARE SECTION; | |
73cf66ba | 96 | |
5eaef520 | 97 | entity = cl->entity; |
98 | who = cl->client_id; | |
99 | users_id = *(int *)argv[0]; | |
73cf66ba | 100 | |
5eaef520 | 101 | EXEC SQL UPDATE users SET fmodtime = SYSDATE, fmodby = :who, |
102 | fmodwith = :entity WHERE users_id = :users_id; | |
73cf66ba | 103 | |
5eaef520 | 104 | return MR_SUCCESS; |
73cf66ba | 105 | } |
106 | ||
107 | ||
108 | /* Sets the pobox modtime on a user record. The users_id will be in argv[0]. | |
109 | */ | |
110 | ||
5eaef520 | 111 | int set_pobox_modtime(struct query *q, char *argv[], client *cl) |
73cf66ba | 112 | { |
5eaef520 | 113 | EXEC SQL BEGIN DECLARE SECTION; |
114 | int users_id, who; | |
115 | char *entity; | |
116 | EXEC SQL END DECLARE SECTION; | |
73cf66ba | 117 | |
5eaef520 | 118 | entity = cl->entity; |
119 | who = cl->client_id; | |
120 | users_id = *(int *)argv[0]; | |
73cf66ba | 121 | |
5eaef520 | 122 | EXEC SQL UPDATE users SET pmodtime = SYSDATE, pmodby = :who, |
123 | pmodwith = :entity WHERE users_id = :users_id; | |
73cf66ba | 124 | |
5eaef520 | 125 | return MR_SUCCESS; |
73cf66ba | 126 | } |
127 | ||
128 | ||
129 | /* Like set_modtime, but uppercases the name first. | |
130 | */ | |
131 | ||
5eaef520 | 132 | int set_uppercase_modtime(struct query *q, char *argv[], client *cl) |
73cf66ba | 133 | { |
5eaef520 | 134 | char *name, *entity, *table; |
135 | int who; | |
73cf66ba | 136 | |
5eaef520 | 137 | entity = cl->entity; |
138 | who = cl->client_id; | |
139 | table = table_name[q->rtable]; | |
140 | name = argv[0]; | |
73cf66ba | 141 | |
5eaef520 | 142 | sprintf(stmt_buf, "UPDATE %s SET modtime = SYSDATE, modby = %d, " |
143 | "modwith = '%s' WHERE name = UPPER('%s')", table, who, entity, name); | |
144 | EXEC SQL EXECUTE IMMEDIATE :stmt_buf; | |
73cf66ba | 145 | |
5eaef520 | 146 | return MR_SUCCESS; |
73cf66ba | 147 | } |
148 | ||
149 | ||
150 | /* Sets the modtime on the machine whose mach_id is in argv[0]. This routine | |
151 | * is necessary for add_machine_to_cluster becuase the table that query | |
152 | * operates on is "mcm", not "machine". | |
153 | */ | |
154 | ||
5eaef520 | 155 | int set_mach_modtime_by_id(struct query *q, char *argv[], client *cl) |
73cf66ba | 156 | { |
5eaef520 | 157 | EXEC SQL BEGIN DECLARE SECTION; |
158 | char *entity; | |
159 | int who, id; | |
160 | EXEC SQL END DECLARE SECTION; | |
161 | ||
162 | entity = cl->entity; | |
163 | who = cl->client_id; | |
164 | id = *(int *)argv[0]; | |
165 | EXEC SQL UPDATE machine SET modtime = SYSDATE, modby = :who, | |
166 | modwith = :entity WHERE mach_id = :id; | |
167 | ||
168 | return MR_SUCCESS; | |
73cf66ba | 169 | } |
170 | ||
171 | ||
172 | /* Sets the modtime on the cluster whose mach_id is in argv[0]. This routine | |
173 | * is necessary for add_cluster_data and delete_cluster_data becuase the | |
174 | * table that query operates on is "svc", not "cluster". | |
175 | */ | |
176 | ||
5eaef520 | 177 | int set_cluster_modtime_by_id(struct query *q, char *argv[], client *cl) |
73cf66ba | 178 | { |
5eaef520 | 179 | EXEC SQL BEGIN DECLARE SECTION; |
180 | char *entity; | |
181 | int who, id; | |
182 | EXEC SQL END DECLARE SECTION; | |
183 | ||
184 | entity = cl->entity; | |
185 | who = cl->client_id; | |
186 | ||
187 | id = *(int *)argv[0]; | |
188 | EXEC SQL UPDATE clusters SET modtime = SYSDATE, modby = :who, | |
189 | modwith = :entity WHERE clu_id = :id; | |
190 | return MR_SUCCESS; | |
73cf66ba | 191 | } |
192 | ||
193 | ||
194 | /* sets the modtime on the serverhost where the service name is in argv[0] | |
195 | * and the mach_id is in argv[1]. | |
196 | */ | |
197 | ||
5eaef520 | 198 | int set_serverhost_modtime(struct query *q, char *argv[], client *cl) |
73cf66ba | 199 | { |
5eaef520 | 200 | EXEC SQL BEGIN DECLARE SECTION; |
201 | char *entity, *serv; | |
202 | int who, id; | |
203 | EXEC SQL END DECLARE SECTION; | |
204 | ||
205 | entity = cl->entity; | |
206 | who = cl->client_id; | |
207 | ||
208 | serv = argv[0]; | |
209 | id = *(int *)argv[1]; | |
210 | EXEC SQL UPDATE serverhosts | |
211 | SET modtime = SYSDATE, modby = :who, modwith = :entity | |
212 | WHERE service = :serv AND mach_id = :id; | |
213 | return MR_SUCCESS; | |
73cf66ba | 214 | } |
215 | ||
216 | ||
217 | /* sets the modtime on the nfsphys where the mach_id is in argv[0] and the | |
218 | * directory name is in argv[1]. | |
219 | */ | |
220 | ||
5eaef520 | 221 | int set_nfsphys_modtime(struct query *q, char *argv[], client *cl) |
73cf66ba | 222 | { |
5eaef520 | 223 | EXEC SQL BEGIN DECLARE SECTION; |
224 | char *entity, *dir; | |
225 | int who, id; | |
226 | EXEC SQL END DECLARE SECTION; | |
227 | ||
228 | entity = cl->entity; | |
229 | who = cl->client_id; | |
230 | ||
231 | id = *(int *)argv[0]; | |
232 | dir = argv[1]; | |
233 | EXEC SQL UPDATE nfsphys SET modtime = SYSDATE, modby = :who, | |
234 | modwith = :entity WHERE dir = :dir AND mach_id = :id; | |
235 | return MR_SUCCESS; | |
73cf66ba | 236 | } |
237 | ||
238 | ||
239 | /* sets the modtime on a filesystem, where argv[0] contains the filesys | |
240 | * label. | |
241 | */ | |
242 | ||
5eaef520 | 243 | int set_filesys_modtime(struct query *q, char *argv[], client *cl) |
73cf66ba | 244 | { |
5eaef520 | 245 | EXEC SQL BEGIN DECLARE SECTION; |
246 | char *label, *entity; | |
247 | int who; | |
248 | extern int _var_phys_id; | |
249 | EXEC SQL END DECLARE SECTION; | |
250 | ||
251 | entity = cl->entity; | |
252 | who = cl->client_id; | |
253 | ||
254 | label = argv[0]; | |
255 | if (!strcmp(q->shortname, "ufil")) | |
256 | label = argv[1]; | |
257 | ||
258 | EXEC SQL UPDATE filesys SET modtime = SYSDATE, modby = :who, | |
259 | modwith = :entity, phys_id = :_var_phys_id | |
260 | WHERE label = :label; | |
261 | return MR_SUCCESS; | |
73cf66ba | 262 | } |
263 | ||
264 | ||
265 | /* sets the modtime on a zephyr class, where argv[0] contains the class | |
266 | * name. | |
267 | */ | |
268 | ||
5eaef520 | 269 | int set_zephyr_modtime(struct query *q, char *argv[], client *cl) |
73cf66ba | 270 | { |
5eaef520 | 271 | EXEC SQL BEGIN DECLARE SECTION; |
272 | char *class, *entity; | |
273 | int who; | |
274 | EXEC SQL END DECLARE SECTION; | |
73cf66ba | 275 | |
5eaef520 | 276 | entity = cl->entity; |
277 | who = cl->client_id; | |
73cf66ba | 278 | |
5eaef520 | 279 | class = argv[0]; |
73cf66ba | 280 | |
5eaef520 | 281 | EXEC SQL UPDATE zephyr SET modtime = SYSDATE, modby = :who, |
282 | modwith = :entity WHERE class = :class; | |
73cf66ba | 283 | |
5eaef520 | 284 | return MR_SUCCESS; |
73cf66ba | 285 | } |
286 | ||
69eb9470 | 287 | /* sets the modtime on an entry in services table, where argv[0] contains |
288 | * the service name. | |
289 | */ | |
290 | ||
291 | int set_service_modtime(struct query *q, char *argv[], client *cl) | |
292 | { | |
293 | EXEC SQL BEGIN DECLARE SECTION; | |
294 | char *service, *protocol, *entity; | |
295 | int who; | |
296 | EXEC SQL END DECLARE SECTION; | |
297 | ||
298 | entity = cl->entity; | |
299 | who = cl->client_id; | |
300 | ||
301 | service = argv[0]; | |
302 | protocol = argv[1]; | |
303 | ||
304 | EXEC SQL UPDATE services SET modtime = SYSDATE, modby = :who, | |
305 | modwith = :entity WHERE name = :service AND protocol = :protocol; | |
306 | ||
307 | return MR_SUCCESS; | |
308 | } | |
73cf66ba | 309 | |
310 | /* fixes the modby field. This will be the second to last thing in the | |
311 | * argv, the argv length is determined from the query structure. It is | |
312 | * passed as a pointer to an integer. This will either turn it into a | |
313 | * username, or # + the users_id. | |
314 | */ | |
5eaef520 | 315 | int followup_fix_modby(struct query *q, struct save_queue *sq, |
7ac48069 | 316 | struct validate *v, int (*action)(int, char *[], void *), |
317 | void *actarg, client *cl) | |
73cf66ba | 318 | { |
44d12d58 | 319 | int i, j; |
5eaef520 | 320 | char **argv; |
321 | int id, status; | |
322 | ||
323 | i = q->vcnt - 2; | |
324 | while (sq_get_data(sq, &argv)) | |
325 | { | |
326 | id = atoi(argv[i]); | |
327 | if (id > 0) | |
328 | status = id_to_name(id, USERS_TABLE, &argv[i]); | |
329 | else | |
330 | status = id_to_name(-id, STRINGS_TABLE, &argv[i]); | |
331 | if (status && status != MR_NO_MATCH) | |
332 | return status; | |
333 | (*action)(q->vcnt, argv, actarg); | |
334 | for (j = 0; j < q->vcnt; j++) | |
335 | free(argv[j]); | |
336 | free(argv); | |
73cf66ba | 337 | } |
5eaef520 | 338 | sq_destroy(sq); |
339 | return MR_SUCCESS; | |
73cf66ba | 340 | } |
341 | ||
73cf66ba | 342 | /** |
343 | ** followup_ausr - add finger and pobox entries, set_user_modtime | |
344 | ** | |
345 | ** Inputs: | |
346 | ** argv[0] - login (add_user) | |
186dd63b | 347 | ** argv[U_LAST] - last name |
348 | ** argv[U_FIRST] - first name | |
349 | ** argv[U_MIDDLE] - middle name | |
73cf66ba | 350 | ** |
351 | **/ | |
352 | ||
5eaef520 | 353 | int followup_ausr(struct query *q, char *argv[], client *cl) |
73cf66ba | 354 | { |
5eaef520 | 355 | EXEC SQL BEGIN DECLARE SECTION; |
356 | int who, status; | |
357 | char *login, *entity, *name; | |
e688520a | 358 | char fullname[USERS_FIRST_SIZE + USERS_MIDDLE_SIZE + USERS_LAST_SIZE]; |
5eaef520 | 359 | EXEC SQL END DECLARE SECTION; |
73cf66ba | 360 | |
5eaef520 | 361 | /* build fullname */ |
186dd63b | 362 | if (strlen(argv[U_FIRST]) && strlen(argv[U_MIDDLE])) |
363 | sprintf(fullname, "%s %s %s", argv[U_FIRST], argv[U_MIDDLE], | |
364 | argv[U_LAST]); | |
365 | else if (strlen(argv[U_FIRST])) | |
366 | sprintf(fullname, "%s %s", argv[U_FIRST], argv[U_LAST]); | |
5eaef520 | 367 | else |
186dd63b | 368 | sprintf(fullname, "%s", argv[U_LAST]); |
73cf66ba | 369 | |
5eaef520 | 370 | login = argv[0]; |
371 | who = cl->client_id; | |
372 | entity = cl->entity; | |
73cf66ba | 373 | |
5eaef520 | 374 | /* create finger entry, pobox & set modtime on user */ |
5eaef520 | 375 | EXEC SQL UPDATE users |
376 | SET modtime = SYSDATE, modby = :who, modwith = :entity, | |
377 | fullname = NVL(:fullname, CHR(0)), affiliation = type, | |
378 | fmodtime = SYSDATE, fmodby = :who, fmodwith = :entity, | |
379 | potype = 'NONE', pmodtime = SYSDATE, pmodby = :who, pmodwith = :entity | |
380 | WHERE login = :login; | |
5eaef520 | 381 | |
5eaef520 | 382 | return MR_SUCCESS; |
73cf66ba | 383 | } |
5eaef520 | 384 | |
d6d830a1 | 385 | /* followup_gpob: fixes argv[2] and argv[3] based on the pobox type. |
386 | * Then completes the upcall to the user. | |
73cf66ba | 387 | * |
d6d830a1 | 388 | * argv[2] is the users_id on input and should be converted to the |
389 | * pobox name on output. argv[3] is empty on input and should be | |
390 | * converted to an email address on output. | |
73cf66ba | 391 | */ |
392 | ||
5eaef520 | 393 | int followup_gpob(struct query *q, struct save_queue *sq, struct validate *v, |
7ac48069 | 394 | int (*action)(int, char *[], void *), void *actarg, |
395 | client *cl) | |
73cf66ba | 396 | { |
5eaef520 | 397 | char **argv; |
398 | char *ptype, *p; | |
399 | int mid, sid, status, i; | |
d6d830a1 | 400 | EXEC SQL BEGIN DECLARE SECTION; |
22bbb6f0 | 401 | int users_id, pid, iid, bid, eid; |
d6d830a1 | 402 | char mach[MACHINE_NAME_SIZE], fs[FILESYS_LABEL_SIZE]; |
403 | char str[STRINGS_STRING_SIZE]; | |
404 | EXEC SQL END DECLARE SECTION; | |
5eaef520 | 405 | |
406 | /* for each row */ | |
407 | while (sq_get_data(sq, &argv)) | |
408 | { | |
d6d830a1 | 409 | mr_trim_args(4, argv); |
5eaef520 | 410 | ptype = argv[1]; |
d6d830a1 | 411 | users_id = atoi(argv[2]); |
5eaef520 | 412 | |
02cf90a4 | 413 | EXEC SQL SELECT pop_id, imap_id, box_id, exchange_id INTO :pid, :iid, :bid, :eid |
450849e9 | 414 | FROM users WHERE users_id = :users_id; |
415 | if (sqlca.sqlcode) | |
416 | return MR_USER; | |
417 | ||
418 | if (ptype[0] == 'S') | |
419 | { | |
420 | /* SMTP or SPLIT */ | |
421 | EXEC SQL SELECT string INTO :str FROM strings | |
422 | WHERE string_id = :bid; | |
423 | if (sqlca.sqlcode) | |
424 | return MR_STRING; | |
425 | ||
426 | /* If SMTP, don't bother fetching IMAP and POP boxes. */ | |
427 | if (ptype[1] == 'M') | |
22bbb6f0 | 428 | pid = iid = eid = 0; |
450849e9 | 429 | } |
430 | if (iid) | |
5eaef520 | 431 | { |
450849e9 | 432 | /* IMAP, or SPLIT with IMAP */ |
433 | EXEC SQL SELECT f.label, m.name INTO :fs, :mach | |
434 | FROM filesys f, machine m | |
435 | WHERE f.filsys_id = :iid AND f.mach_id = m.mach_id; | |
436 | if (sqlca.sqlcode) | |
437 | return MR_FILESYS; | |
438 | } | |
439 | if (pid) | |
440 | { | |
441 | /* POP, or SPLIT with POP */ | |
d6d830a1 | 442 | EXEC SQL SELECT m.name INTO :mach FROM machine m, users u |
443 | WHERE u.users_id = :users_id AND u.pop_id = m.mach_id; | |
444 | if (sqlca.sqlcode) | |
5eaef520 | 445 | return MR_MACHINE; |
450849e9 | 446 | } |
22bbb6f0 | 447 | if (eid) |
448 | { | |
449 | /* EXCHANGE, or SPLIT with EXCHANGE */ | |
450 | EXEC SQL SELECT m.name INTO :mach FROM machine m, users u | |
451 | WHERE u.users_id = :users_id AND u.exchange_id = m.mach_id; | |
452 | if (sqlca.sqlcode) | |
453 | return MR_MACHINE; | |
454 | } | |
450849e9 | 455 | |
456 | free(argv[2]); | |
457 | free(argv[3]); | |
458 | ||
459 | /* Now assemble the right answer. */ | |
22bbb6f0 | 460 | if (!strcmp(ptype, "POP") || !strcmp(ptype, "EXCHANGE")) |
450849e9 | 461 | { |
d6d830a1 | 462 | argv[2] = xstrdup(strtrim(mach)); |
d6d830a1 | 463 | argv[3] = xmalloc(strlen(argv[0]) + strlen(argv[2]) + 2); |
464 | sprintf(argv[3], "%s@%s", argv[0], argv[2]); | |
5eaef520 | 465 | } |
466 | else if (!strcmp(ptype, "SMTP")) | |
467 | { | |
d6d830a1 | 468 | argv[2] = xstrdup(strtrim(str)); |
469 | argv[3] = xstrdup(str); | |
73cf66ba | 470 | } |
f76b37f2 | 471 | else if (!strcmp(ptype, "IMAP")) |
472 | { | |
d6d830a1 | 473 | argv[2] = xstrdup(strtrim(fs)); |
d6d830a1 | 474 | argv[3] = xmalloc(strlen(argv[0]) + strlen(strtrim(mach)) + 2); |
475 | sprintf(argv[3], "%s@%s", argv[0], mach); | |
f76b37f2 | 476 | } |
450849e9 | 477 | else if (!strcmp(ptype, "SPLIT")) |
478 | { | |
479 | argv[2] = xstrdup(strtrim(str)); | |
480 | argv[3] = xmalloc(strlen(argv[0]) + strlen(strtrim(mach)) + | |
481 | strlen(str) + 4); | |
482 | sprintf(argv[3], "%s@%s, %s", argv[0], mach, str); | |
483 | } | |
5eaef520 | 484 | else /* ptype == "NONE" */ |
485 | goto skip; | |
5eaef520 | 486 | |
487 | if (!strcmp(q->shortname, "gpob")) | |
488 | { | |
d6d830a1 | 489 | sid = atoi(argv[5]); |
5eaef520 | 490 | if (sid > 0) |
d6d830a1 | 491 | status = id_to_name(sid, USERS_TABLE, &argv[5]); |
5eaef520 | 492 | else |
d6d830a1 | 493 | status = id_to_name(-sid, STRINGS_TABLE, &argv[5]); |
494 | if (status && status != MR_NO_MATCH) | |
495 | return status; | |
73cf66ba | 496 | } |
73cf66ba | 497 | |
5eaef520 | 498 | (*action)(q->vcnt, argv, actarg); |
73cf66ba | 499 | skip: |
5eaef520 | 500 | /* free saved data */ |
501 | for (i = 0; i < q->vcnt; i++) | |
502 | free(argv[i]); | |
503 | free(argv); | |
73cf66ba | 504 | } |
505 | ||
5eaef520 | 506 | sq_destroy(sq); |
507 | return MR_SUCCESS; | |
73cf66ba | 508 | } |
509 | ||
b121cf1b | 510 | /* Fix an ace_name, based on its type. */ |
511 | ||
512 | static int fix_ace(char *type, char **name) | |
513 | { | |
514 | int id = atoi(*name); | |
515 | ||
516 | if (!strcmp(type, "LIST")) | |
517 | return id_to_name(id, LIST_TABLE, name); | |
518 | else if (!strcmp(type, "USER")) | |
519 | return id_to_name(id, USERS_TABLE, name); | |
520 | else if (!strcmp(type, "KERBEROS")) | |
521 | return id_to_name(id, STRINGS_TABLE, name); | |
522 | else | |
523 | { | |
524 | free(*name); | |
525 | if (!strcmp(type, "NONE")) | |
526 | *name = xstrdup("NONE"); | |
527 | else | |
528 | *name = xstrdup("???"); | |
529 | return MR_SUCCESS; | |
530 | } | |
531 | } | |
73cf66ba | 532 | |
b121cf1b | 533 | |
534 | /* followup_gsnt: fix the ace_name and modby */ | |
73cf66ba | 535 | |
5eaef520 | 536 | int followup_gsnt(struct query *q, struct save_queue *sq, struct validate *v, |
7ac48069 | 537 | int (*action)(int, char *[], void *), void *actarg, |
538 | client *cl) | |
73cf66ba | 539 | { |
b121cf1b | 540 | char **argv; |
4f6b1a05 | 541 | int status, idx; |
542 | ||
543 | if (q->version < 8) | |
544 | idx = 0; | |
545 | else | |
546 | idx = 3; | |
5eaef520 | 547 | |
548 | while (sq_get_data(sq, &argv)) | |
549 | { | |
550 | mr_trim_args(q->vcnt, argv); | |
551 | ||
4f6b1a05 | 552 | status = fix_ace(argv[7 + idx], &argv[8 + idx]); |
5eaef520 | 553 | if (status && status != MR_NO_MATCH) |
554 | return status; | |
fde7313c | 555 | } |
556 | ||
b121cf1b | 557 | return followup_fix_modby(q, sq, v, action, actarg, cl); |
fde7313c | 558 | } |
559 | ||
560 | ||
b121cf1b | 561 | /* followup_ghst: fix the ace_name, strings and modby */ |
fde7313c | 562 | |
5eaef520 | 563 | int followup_ghst(struct query *q, struct save_queue *sq, struct validate *v, |
7ac48069 | 564 | int (*action)(int, char *[], void *), void *actarg, |
565 | client *cl) | |
fde7313c | 566 | { |
b121cf1b | 567 | char **argv; |
e4ae0190 | 568 | int id, status, idx; |
569 | ||
570 | if (q->version < 6) | |
571 | idx = 0; | |
4f6b1a05 | 572 | else if (q->version >= 6 && q->version < 8) |
e4ae0190 | 573 | idx = 1; |
4f6b1a05 | 574 | else |
575 | idx = 2; | |
5eaef520 | 576 | |
577 | while (sq_get_data(sq, &argv)) | |
578 | { | |
579 | mr_trim_args(q->vcnt, argv); | |
580 | ||
e4ae0190 | 581 | id = atoi(argv[13 + idx]); |
582 | status = id_to_name(id, STRINGS_TABLE, &argv[13 + idx]); | |
5eaef520 | 583 | if (status) |
584 | return status; | |
e4ae0190 | 585 | id = atoi(argv[14 + idx]); |
586 | status = id_to_name(id, STRINGS_TABLE, &argv[14 + idx]); | |
5eaef520 | 587 | if (status) |
588 | return status; | |
e4ae0190 | 589 | id = atoi(argv[16 + idx]); |
5eaef520 | 590 | if (id < 0) |
e4ae0190 | 591 | status = id_to_name(-id, STRINGS_TABLE, &argv[16 + idx]); |
5eaef520 | 592 | else |
e4ae0190 | 593 | status = id_to_name(id, USERS_TABLE, &argv[16 + idx]); |
5eaef520 | 594 | if (status && status != MR_NO_MATCH) |
595 | return status; | |
596 | ||
e4ae0190 | 597 | status = fix_ace(argv[11 + idx], &argv[12 + idx]); |
5eaef520 | 598 | if (status && status != MR_NO_MATCH) |
599 | return status; | |
73cf66ba | 600 | } |
601 | ||
b121cf1b | 602 | return followup_fix_modby(q, sq, v, action, actarg, cl); |
73cf66ba | 603 | } |
604 | ||
605 | ||
b121cf1b | 606 | /* followup_glin: fix the ace_name, modace_name, expiration, and modby */ |
73cf66ba | 607 | |
5eaef520 | 608 | int followup_glin(struct query *q, struct save_queue *sq, struct validate *v, |
7ac48069 | 609 | int (*action)(int, char *[], void *), void *actarg, |
610 | client *cl) | |
73cf66ba | 611 | { |
b121cf1b | 612 | char **argv; |
59c3208b | 613 | int status; |
5eaef520 | 614 | |
615 | while (sq_get_data(sq, &argv)) | |
616 | { | |
617 | mr_trim_args(q->vcnt, argv); | |
618 | ||
59c3208b | 619 | if (q->version == 2) |
620 | status = fix_ace(argv[7], &argv[8]); | |
8e3761a2 | 621 | else if (q->version > 2 && q->version < 10) |
59c3208b | 622 | status = fix_ace(argv[8], &argv[9]); |
8e3761a2 | 623 | else |
624 | status = fix_ace(argv[10], &argv[11]); | |
625 | ||
626 | if (status && status != MR_NO_MATCH) | |
627 | return status; | |
628 | ||
672173f4 | 629 | if (q->version > 3) |
630 | { | |
631 | if (q->version < 10) | |
632 | status = fix_ace(argv[10], &argv[11]); | |
633 | else if (q->version >= 10) | |
634 | status = fix_ace(argv[12], &argv[13]); | |
8e3761a2 | 635 | |
672173f4 | 636 | if (status && status != MR_NO_MATCH) |
637 | return status; | |
638 | } | |
73cf66ba | 639 | |
59c3208b | 640 | if (atoi(argv[6]) == -1) |
5eaef520 | 641 | { |
e688520a | 642 | argv[6] = xrealloc(argv[6], strlen(UNIQUE_GID) + 1); |
5eaef520 | 643 | strcpy(argv[6], UNIQUE_GID); |
73cf66ba | 644 | } |
73cf66ba | 645 | } |
646 | ||
b121cf1b | 647 | return followup_fix_modby(q, sq, v, action, actarg, cl); |
73cf66ba | 648 | } |
649 | ||
59c3208b | 650 | /* followup_gsin: fix the ace_name and modby. */ |
651 | int followup_gsin(struct query *q, struct save_queue *sq, struct validate *v, | |
652 | int (*action)(int, char *[], void *), void *actarg, | |
653 | client *cl) | |
654 | { | |
655 | char **argv; | |
656 | int status; | |
657 | ||
658 | while (sq_get_data(sq, &argv)) | |
659 | { | |
660 | mr_trim_args(q->vcnt, argv); | |
661 | ||
662 | status = fix_ace(argv[11], &argv[12]); | |
663 | if (status && status != MR_NO_MATCH) | |
664 | return status; | |
665 | } | |
666 | ||
667 | return followup_fix_modby(q, sq, v, action, actarg, cl); | |
668 | } | |
669 | ||
1a9a0a59 | 670 | int followup_gpsv(struct query *q, struct save_queue *sq, struct validate *v, |
671 | int (*action)(int, char *[], void *), void *actarg, | |
672 | client *cl) | |
673 | { | |
b121cf1b | 674 | char **argv; |
675 | int status; | |
1a9a0a59 | 676 | |
677 | while (sq_get_data(sq, &argv)) | |
678 | { | |
679 | mr_trim_args(q->vcnt, argv); | |
680 | ||
b121cf1b | 681 | status = fix_ace(argv[PRINTSERVER_OWNER_TYPE], |
682 | &argv[PRINTSERVER_OWNER_NAME]); | |
1a9a0a59 | 683 | if (status && status != MR_NO_MATCH) |
684 | return status; | |
685 | } | |
686 | ||
687 | return followup_fix_modby(q, sq, v, action, actarg, cl); | |
688 | } | |
689 | ||
73cf66ba | 690 | |
691 | /* followup_gqot: Fix the entity name, directory name & modby fields | |
692 | * argv[0] = filsys_id | |
693 | * argv[1] = type | |
694 | * argv[2] = entity_id | |
695 | * argv[3] = ascii(quota) | |
696 | */ | |
697 | ||
5eaef520 | 698 | int followup_gqot(struct query *q, struct save_queue *sq, struct validate *v, |
7ac48069 | 699 | int (*action)(int, char *[], void *), void *actarg, |
700 | client *cl) | |
73cf66ba | 701 | { |
44d12d58 | 702 | int j; |
5eaef520 | 703 | char **argv; |
704 | EXEC SQL BEGIN DECLARE SECTION; | |
705 | int id; | |
706 | char *name, *label; | |
707 | EXEC SQL END DECLARE SECTION; | |
708 | int status, idx; | |
709 | ||
710 | if (!strcmp(q->name, "get_quota") || | |
711 | !strcmp(q->name, "get_quota_by_filesys")) | |
712 | idx = 4; | |
713 | else | |
714 | idx = 3; | |
b121cf1b | 715 | |
5eaef520 | 716 | while (sq_get_data(sq, &argv)) |
717 | { | |
718 | if (idx == 4) | |
719 | { | |
720 | switch (argv[1][0]) | |
721 | { | |
73cf66ba | 722 | case 'U': |
5eaef520 | 723 | status = id_to_name(atoi(argv[2]), USERS_TABLE, &argv[2]); |
724 | break; | |
73cf66ba | 725 | case 'G': |
726 | case 'L': | |
5eaef520 | 727 | status = id_to_name(atoi(argv[2]), LIST_TABLE, &argv[2]); |
728 | break; | |
73cf66ba | 729 | case 'A': |
5eaef520 | 730 | free(argv[2]); |
e688520a | 731 | argv[2] = xstrdup("system:anyuser"); |
5eaef520 | 732 | break; |
73cf66ba | 733 | default: |
5eaef520 | 734 | id = atoi(argv[2]); |
e688520a | 735 | argv[2] = xmalloc(8); |
5eaef520 | 736 | sprintf(argv[2], "%d", id); |
73cf66ba | 737 | } |
738 | } | |
5eaef520 | 739 | id = atoi(argv[idx]); |
740 | free(argv[idx]); | |
e688520a | 741 | argv[idx] = xmalloc(id ? NFSPHYS_DIR_SIZE : FILESYS_NAME_SIZE); |
5eaef520 | 742 | name = argv[idx]; |
e688520a | 743 | name[0] = '\0'; |
5eaef520 | 744 | if (id == 0) |
745 | { | |
746 | label = argv[0]; | |
747 | EXEC SQL SELECT name INTO :name FROM filesys | |
748 | WHERE label = :label; | |
73cf66ba | 749 | } |
5eaef520 | 750 | else |
751 | { | |
752 | EXEC SQL SELECT dir INTO :name FROM nfsphys | |
753 | WHERE nfsphys_id = :id; | |
73cf66ba | 754 | } |
5eaef520 | 755 | if (sqlca.sqlerrd[2] != 1) |
756 | sprintf(argv[idx], "#%d", id); | |
757 | ||
758 | id = atoi(argv[idx + 3]); | |
759 | if (id > 0) | |
760 | status = id_to_name(id, USERS_TABLE, &argv[idx + 3]); | |
761 | else | |
762 | status = id_to_name(-id, STRINGS_TABLE, &argv[idx + 3]); | |
763 | if (status && status != MR_NO_MATCH) | |
764 | return status; | |
765 | (*action)(q->vcnt, argv, actarg); | |
766 | for (j = 0; j < q->vcnt; j++) | |
767 | free(argv[j]); | |
768 | free(argv); | |
73cf66ba | 769 | } |
5eaef520 | 770 | sq_destroy(sq); |
771 | return MR_SUCCESS; | |
73cf66ba | 772 | } |
773 | ||
774 | ||
775 | /* followup_aqot: Add allocation to nfsphys after creating quota. | |
776 | * argv[0] = filsys_id | |
777 | * argv[1] = type if "add_quota" or "update_quota" | |
778 | * argv[2 or 1] = id | |
779 | * argv[3 or 2] = ascii(quota) | |
780 | */ | |
781 | ||
5eaef520 | 782 | int followup_aqot(struct query *q, char *argv[], client *cl) |
73cf66ba | 783 | { |
5eaef520 | 784 | EXEC SQL BEGIN DECLARE SECTION; |
785 | int quota, id, fs, who, physid, table; | |
786 | char *entity, *qtype, *tname; | |
787 | EXEC SQL END DECLARE SECTION; | |
788 | char incr_qual[60]; | |
789 | char *incr_argv[2]; | |
790 | int status; | |
791 | ||
792 | table = q->rtable; | |
793 | tname = table_name[table]; | |
794 | fs = *(int *)argv[0]; | |
795 | EXEC SQL SELECT phys_id INTO :physid FROM filesys | |
796 | WHERE filsys_id = :fs; | |
797 | if (dbms_errno) | |
798 | return mr_errcode; | |
799 | ||
800 | if (!strcmp(q->shortname, "aqot") || !strcmp(q->shortname, "uqot")) | |
801 | { | |
802 | qtype = argv[1]; | |
803 | id = *(int *)argv[2]; | |
804 | quota = atoi(argv[3]); | |
805 | sprintf(incr_qual, "q.filsys_id = %d", fs); | |
806 | } | |
807 | else | |
808 | { | |
809 | qtype = "USER"; | |
810 | id = *(int *)argv[1]; | |
811 | quota = atoi(argv[2]); | |
812 | sprintf(incr_qual, "q.filsys_id = %d AND q.type = '%s' AND " | |
813 | "q.entity_id = %d", fs, qtype, id); | |
73cf66ba | 814 | } |
815 | ||
5eaef520 | 816 | /* quota case of incremental_{before|after} only looks at slot 1 */ |
817 | incr_argv[1] = qtype; | |
818 | ||
819 | /* Follows one of many possible gross hacks to fix these particular | |
820 | * conflicts between what is possible in the query table and what | |
821 | * is possible in SQL. | |
822 | */ | |
b9f9ab1c | 823 | if (q->type == MR_Q_APPEND) |
5eaef520 | 824 | { |
825 | incremental_clear_before(); | |
826 | EXEC SQL INSERT INTO quota | |
827 | (filsys_id, type, entity_id, quota, phys_id) | |
828 | VALUES (:fs, NVL(:qtype, CHR(0)), :id, :quota, :physid); | |
829 | incremental_after(table, incr_qual, incr_argv); | |
830 | } | |
831 | else | |
832 | { | |
833 | incremental_before(table, incr_qual, incr_argv); | |
834 | EXEC SQL UPDATE quota SET quota = :quota | |
835 | WHERE filsys_id = :fs AND type = :qtype AND entity_id = :id; | |
836 | status = mr_errcode; | |
837 | incremental_after(table, incr_qual, incr_argv); | |
73cf66ba | 838 | } |
839 | ||
5eaef520 | 840 | if (dbms_errno) |
841 | return mr_errcode; | |
b9f9ab1c | 842 | if (q->type == MR_Q_APPEND) |
5eaef520 | 843 | { |
844 | EXEC SQL UPDATE tblstats SET appends = appends + 1, modtime = SYSDATE | |
845 | WHERE table_name = :tname; | |
846 | } | |
847 | else | |
848 | { | |
849 | EXEC SQL UPDATE tblstats SET updates = updates + 1, modtime = SYSDATE | |
850 | WHERE table_name = :tname; | |
73cf66ba | 851 | } |
5eaef520 | 852 | |
853 | /* Proceed with original followup */ | |
854 | who = cl->client_id; | |
855 | entity = cl->entity; | |
856 | ||
857 | EXEC SQL UPDATE quota | |
858 | SET modtime = SYSDATE, modby = :who, modwith = :entity | |
859 | WHERE filsys_id = :fs and type = :qtype and entity_id = :id; | |
860 | EXEC SQL UPDATE nfsphys SET allocated = allocated + :quota | |
861 | WHERE nfsphys_id = :physid; | |
862 | if (dbms_errno) | |
863 | return mr_errcode; | |
864 | return MR_SUCCESS; | |
73cf66ba | 865 | } |
866 | ||
867 | ||
868 | /* Necessitated by the requirement of a correlation name by the incremental | |
5eaef520 | 869 | * routines, since query table deletes don't provide one. |
73cf66ba | 870 | */ |
5eaef520 | 871 | int followup_dqot(struct query *q, char **argv, client *cl) |
73cf66ba | 872 | { |
5eaef520 | 873 | char *qtype; |
874 | int id, fs, table; | |
875 | char *incr_argv[2]; | |
876 | EXEC SQL BEGIN DECLARE SECTION; | |
877 | char incr_qual[80], *tname; | |
878 | EXEC SQL END DECLARE SECTION; | |
879 | ||
880 | table = q->rtable; | |
881 | tname = table_name[table]; | |
882 | fs = *(int *)argv[0]; | |
883 | if (!strcmp(q->shortname, "dqot")) | |
884 | { | |
885 | qtype = argv[1]; | |
886 | id = *(int *)argv[2]; | |
887 | } | |
888 | else | |
889 | { | |
890 | qtype = "USER"; | |
891 | id = *(int *)argv[1]; | |
73cf66ba | 892 | } |
5eaef520 | 893 | sprintf(incr_qual, "q.filsys_id = %d AND q.type = '%s' AND q.entity_id = %d", |
894 | fs, qtype, id); | |
73cf66ba | 895 | |
5eaef520 | 896 | /* quota case of incremental_{before|after} only looks at slot 1 */ |
897 | incr_argv[1] = qtype; | |
73cf66ba | 898 | |
5eaef520 | 899 | incremental_before(table, incr_qual, incr_argv); |
900 | EXEC SQL DELETE FROM quota q WHERE q.filsys_id = :fs AND q.type = :qtype | |
901 | AND q.entity_id = :id; | |
902 | incremental_clear_after(); | |
73cf66ba | 903 | |
5eaef520 | 904 | if (dbms_errno) |
905 | return mr_errcode; | |
73cf66ba | 906 | |
5eaef520 | 907 | EXEC SQL UPDATE tblstats SET deletes = deletes + 1, modtime = SYSDATE |
908 | WHERE table_name = :tname; | |
909 | return MR_SUCCESS; | |
73cf66ba | 910 | } |
911 | ||
73cf66ba | 912 | /* followup_gzcl: |
913 | */ | |
914 | ||
5eaef520 | 915 | int followup_gzcl(struct query *q, struct save_queue *sq, struct validate *v, |
7ac48069 | 916 | int (*action)(int, char *[], void *), void *actarg, |
917 | client *cl) | |
73cf66ba | 918 | { |
d7ddc011 | 919 | int i, n, status; |
5eaef520 | 920 | char **argv; |
921 | ||
d7ddc011 | 922 | if (q->version < 5) |
923 | n = 8; | |
924 | else | |
925 | n = 10; | |
926 | ||
5eaef520 | 927 | while (sq_get_data(sq, &argv)) |
928 | { | |
929 | mr_trim_args(q->vcnt, argv); | |
930 | ||
d7ddc011 | 931 | for (i = 1; i < n; i += 2) |
5eaef520 | 932 | { |
b121cf1b | 933 | status = fix_ace(argv[i], &argv[i + 1]); |
5eaef520 | 934 | if (status && status != MR_NO_MATCH) |
935 | return status; | |
73cf66ba | 936 | } |
73cf66ba | 937 | } |
b121cf1b | 938 | |
939 | return followup_fix_modby(q, sq, v, action, actarg, cl); | |
73cf66ba | 940 | } |
941 | ||
942 | ||
943 | /* followup_gsha: | |
944 | */ | |
945 | ||
5eaef520 | 946 | int followup_gsha(struct query *q, struct save_queue *sq, struct validate *v, |
7ac48069 | 947 | int (*action)(int, char *[], void *), void *actarg, |
948 | client *cl) | |
73cf66ba | 949 | { |
5eaef520 | 950 | char **argv; |
b121cf1b | 951 | int status; |
5eaef520 | 952 | |
953 | while (sq_get_data(sq, &argv)) | |
954 | { | |
955 | mr_trim_args(q->vcnt, argv); | |
956 | ||
b121cf1b | 957 | status = fix_ace(argv[1], &argv[2]); |
5eaef520 | 958 | if (status && status != MR_NO_MATCH) |
959 | return status; | |
73cf66ba | 960 | } |
b121cf1b | 961 | |
962 | return followup_fix_modby(q, sq, v, action, actarg, cl); | |
73cf66ba | 963 | } |
964 | ||
965 | ||
5eaef520 | 966 | int _sdl_followup(struct query *q, char *argv[], client *cl) |
73cf66ba | 967 | { |
85330553 | 968 | if (atoi(argv[0])) |
5eaef520 | 969 | EXEC SQL ALTER SESSION SET SQL_TRACE TRUE; |
970 | else | |
971 | EXEC SQL ALTER SESSION SET SQL_TRACE FALSE; | |
972 | ||
973 | return MR_SUCCESS; | |
73cf66ba | 974 | } |
975 | ||
976 | ||
85330553 | 977 | int trigger_dcm(struct query *q, char *argv[], client *cl) |
73cf66ba | 978 | { |
85330553 | 979 | pid_t pid; |
e688520a | 980 | char prog[MAXPATHLEN]; |
85330553 | 981 | |
982 | sprintf(prog, "%s/startdcm", BIN_DIR); | |
983 | pid = vfork(); | |
984 | switch (pid) | |
5eaef520 | 985 | { |
85330553 | 986 | case 0: |
987 | execl(prog, "startdcm", 0); | |
988 | exit(1); | |
989 | ||
990 | case -1: | |
991 | return errno; | |
992 | ||
73cf66ba | 993 | default: |
85330553 | 994 | return MR_SUCCESS; |
73cf66ba | 995 | } |
996 | } | |
2fb668b0 | 997 | |
998 | /* followup_gcon: fix the ace_name, memace_name, and modby */ | |
999 | ||
1000 | int followup_gcon(struct query *q, struct save_queue *sq, struct validate *v, | |
1001 | int (*action)(int, char *[], void *), void *actarg, | |
1002 | client *cl) | |
1003 | { | |
1004 | char **argv; | |
73155abd | 1005 | int status, idx = 0; |
1006 | ||
1007 | if (q->version >= 9) | |
1008 | idx = 1; | |
2fb668b0 | 1009 | |
1010 | while (sq_get_data(sq, &argv)) | |
1011 | { | |
1012 | mr_trim_args(q->vcnt, argv); | |
1013 | ||
73155abd | 1014 | status = fix_ace(argv[4 + idx], &argv[5 + idx]); |
2fb668b0 | 1015 | if (status && status != MR_NO_MATCH) |
1016 | return status; | |
1017 | ||
73155abd | 1018 | status = fix_ace(argv[6 + idx], &argv[7 + idx]); |
2fb668b0 | 1019 | if (status && status != MR_NO_MATCH) |
1020 | return status; | |
1021 | } | |
1022 | ||
1023 | return followup_fix_modby(q, sq, v, action, actarg, cl); | |
1024 | } | |
1025 | ||
3b634eb3 | 1026 | /* followup_get_user: fix the modby and creator. |
1027 | * This assumes that the modby and creator fields are always | |
1028 | * in the same relative position in the argv. | |
1029 | */ | |
1030 | ||
1031 | int followup_get_user(struct query *q, struct save_queue *sq, struct | |
1032 | validate *v, int (*action)(int, char *[], void *), | |
1033 | void *actarg, client *cl) | |
1034 | { | |
1035 | char **argv; | |
1036 | int i, j, k, status, id; | |
1037 | ||
1038 | i = q->vcnt - 4; | |
1039 | j = q->vcnt - 1; | |
1040 | while (sq_get_data(sq, &argv)) | |
1041 | { | |
1042 | mr_trim_args(q->vcnt, argv); | |
1043 | ||
1044 | id = atoi(argv[i]); | |
1045 | if (id > 0) | |
1046 | status = id_to_name(id, USERS_TABLE, &argv[i]); | |
1047 | else | |
1048 | status = id_to_name(-id, STRINGS_TABLE, &argv[i]); | |
1049 | if (status && status != MR_NO_MATCH) | |
1050 | return status; | |
1051 | ||
1052 | id = atoi(argv[j]); | |
1053 | if (id > 0) | |
1054 | status = id_to_name(id, USERS_TABLE, &argv[j]); | |
1055 | else | |
1056 | status = id_to_name(-id, STRINGS_TABLE, &argv[j]); | |
1057 | if (status && status != MR_NO_MATCH) | |
1058 | return status; | |
1059 | ||
7902e669 | 1060 | if (q->version > 11) |
1061 | { | |
1062 | status = fix_ace(argv[15], &argv[16]); | |
1063 | if (status && status != MR_NO_MATCH) | |
1064 | return status; | |
1065 | } | |
1066 | ||
3b634eb3 | 1067 | (*action)(q->vcnt, argv, actarg); |
1068 | for (k = 0; k < q->vcnt; k++) | |
1069 | free(argv[k]); | |
1070 | free(argv); | |
1071 | } | |
1072 | sq_destroy(sq); | |
1073 | return MR_SUCCESS; | |
1074 | } |