1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #pragma ident "%Z%%M% %I% %E% SMI"
27
28 /*
29 * file_object.c - enter objects into and load them from the backend
30 *
31 * The primary entry points in this layer are object_create(),
32 * object_create_pg(), object_delete(), and object_fill_children(). They each
33 * take an rc_node_t and use the functions in the object_info_t info array for
34 * the node's type.
35 */
36
37 #include <assert.h>
38 #include <pthread.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <strings.h>
43
44 #include "configd.h"
45 #include "repcache_protocol.h"
46
47 typedef struct child_info {
48 rc_node_t *ci_parent;
49 backend_tx_t *ci_tx; /* only for properties */
50 rc_node_lookup_t ci_base_nl;
51 } child_info_t;
52
53 typedef struct delete_ent delete_ent_t;
54 typedef struct delete_stack delete_stack_t;
55 typedef struct delete_info delete_info_t;
56
57 typedef int delete_cb_func(delete_info_t *, const delete_ent_t *);
58
59 struct delete_ent {
60 delete_cb_func *de_cb; /* callback */
61 uint32_t de_backend;
62 uint32_t de_id;
63 uint32_t de_gen; /* only for property groups */
64 };
65
66 struct delete_stack {
67 struct delete_stack *ds_next;
68 uint32_t ds_size; /* number of elements */
69 uint32_t ds_cur; /* current offset */
70 delete_ent_t ds_buf[1]; /* actually ds_size */
71 };
72 #define DELETE_STACK_SIZE(x) offsetof(delete_stack_t, ds_buf[(x)])
73
74 struct delete_info {
75 backend_tx_t *di_tx;
76 backend_tx_t *di_np_tx;
77 delete_stack_t *di_stack;
78 delete_stack_t *di_free;
79 };
80
81 typedef struct object_info {
82 uint32_t obj_type;
83 enum id_space obj_id_space;
84
85 int (*obj_fill_children)(rc_node_t *);
86 int (*obj_setup_child_info)(rc_node_t *, uint32_t, child_info_t *);
87 int (*obj_query_child)(backend_query_t *, rc_node_lookup_t *,
88 const char *);
89 int (*obj_insert_child)(backend_tx_t *, rc_node_lookup_t *,
90 const char *);
91 int (*obj_insert_pg_child)(backend_tx_t *, rc_node_lookup_t *,
92 const char *, const char *, uint32_t, uint32_t);
93 int (*obj_delete_start)(rc_node_t *, delete_info_t *);
94 } object_info_t;
95
96 static void
string_to_id(const char * str,uint32_t * output,const char * fieldname)97 string_to_id(const char *str, uint32_t *output, const char *fieldname)
98 {
99 if (uu_strtouint(str, output, sizeof (*output), 0, 0, 0) == -1)
100 backend_panic("invalid integer \"%s\" in field \"%s\"",
101 str, fieldname);
102 }
103
104 #define NUM_NEEDED 50
105
106 static int
delete_stack_push(delete_info_t * dip,uint32_t be,delete_cb_func * cb,uint32_t id,uint32_t gen)107 delete_stack_push(delete_info_t *dip, uint32_t be, delete_cb_func *cb,
108 uint32_t id, uint32_t gen)
109 {
110 delete_stack_t *cur = dip->di_stack;
111 delete_ent_t *ent;
112
113 if (cur == NULL || cur->ds_cur == cur->ds_size) {
114 delete_stack_t *new = dip->di_free;
115 dip->di_free = NULL;
116 if (new == NULL) {
117 new = uu_zalloc(DELETE_STACK_SIZE(NUM_NEEDED));
118 if (new == NULL)
119 return (REP_PROTOCOL_FAIL_NO_RESOURCES);
120 new->ds_size = NUM_NEEDED;
121 }
122 new->ds_cur = 0;
123 new->ds_next = dip->di_stack;
124 dip->di_stack = new;
125 cur = new;
126 }
127 assert(cur->ds_cur < cur->ds_size);
128 ent = &cur->ds_buf[cur->ds_cur++];
129
130 ent->de_backend = be;
131 ent->de_cb = cb;
132 ent->de_id = id;
133 ent->de_gen = gen;
134
135 return (REP_PROTOCOL_SUCCESS);
136 }
137
138 static int
delete_stack_pop(delete_info_t * dip,delete_ent_t * out)139 delete_stack_pop(delete_info_t *dip, delete_ent_t *out)
140 {
141 delete_stack_t *cur = dip->di_stack;
142 delete_ent_t *ent;
143
144 if (cur == NULL)
145 return (0);
146 assert(cur->ds_cur > 0 && cur->ds_cur <= cur->ds_size);
147 ent = &cur->ds_buf[--cur->ds_cur];
148 if (cur->ds_cur == 0) {
149 dip->di_stack = cur->ds_next;
150 cur->ds_next = NULL;
151
152 if (dip->di_free != NULL)
153 uu_free(dip->di_free);
154 dip->di_free = cur;
155 }
156 if (ent == NULL)
157 return (0);
158
159 *out = *ent;
160 return (1);
161 }
162
163 static void
delete_stack_cleanup(delete_info_t * dip)164 delete_stack_cleanup(delete_info_t *dip)
165 {
166 delete_stack_t *cur;
167 while ((cur = dip->di_stack) != NULL) {
168 dip->di_stack = cur->ds_next;
169
170 uu_free(cur);
171 }
172
173 if ((cur = dip->di_free) != NULL) {
174 assert(cur->ds_next == NULL); /* should only be one */
175 uu_free(cur);
176 dip->di_free = NULL;
177 }
178 }
179
180 struct delete_cb_info {
181 delete_info_t *dci_dip;
182 uint32_t dci_be;
183 delete_cb_func *dci_cb;
184 int dci_result;
185 };
186
187 /*ARGSUSED*/
188 static int
push_delete_callback(void * data,int columns,char ** vals,char ** names)189 push_delete_callback(void *data, int columns, char **vals, char **names)
190 {
191 struct delete_cb_info *info = data;
192
193 const char *id_str = *vals++;
194 const char *gen_str = *vals++;
195
196 uint32_t id;
197 uint32_t gen;
198
199 assert(columns == 2);
200
201 string_to_id(id_str, &id, "id");
202 string_to_id(gen_str, &gen, "gen_id");
203
204 info->dci_result = delete_stack_push(info->dci_dip, info->dci_be,
205 info->dci_cb, id, gen);
206
207 if (info->dci_result != REP_PROTOCOL_SUCCESS)
208 return (BACKEND_CALLBACK_ABORT);
209 return (BACKEND_CALLBACK_CONTINUE);
210 }
211
212 static int
value_delete(delete_info_t * dip,const delete_ent_t * ent)213 value_delete(delete_info_t *dip, const delete_ent_t *ent)
214 {
215 uint32_t be = ent->de_backend;
216 int r;
217
218 backend_query_t *q;
219
220 backend_tx_t *tx = (be == BACKEND_TYPE_NORMAL)? dip->di_tx :
221 dip->di_np_tx;
222
223 q = backend_query_alloc();
224
225 backend_query_add(q,
226 "SELECT 1 FROM prop_lnk_tbl WHERE (lnk_val_id = %d); "
227 "DELETE FROM value_tbl WHERE (value_id = %d); ",
228 ent->de_id, ent->de_id);
229 r = backend_tx_run(tx, q, backend_fail_if_seen, NULL);
230 backend_query_free(q);
231 if (r == REP_PROTOCOL_DONE)
232 return (REP_PROTOCOL_SUCCESS); /* still in use */
233 return (r);
234 }
235
236 static int
pg_lnk_tbl_delete(delete_info_t * dip,const delete_ent_t * ent)237 pg_lnk_tbl_delete(delete_info_t *dip, const delete_ent_t *ent)
238 {
239 struct delete_cb_info info;
240 uint32_t be = ent->de_backend;
241 int r;
242
243 backend_query_t *q;
244
245 backend_tx_t *tx = (be == BACKEND_TYPE_NORMAL)? dip->di_tx :
246 dip->di_np_tx;
247
248 /*
249 * For non-persistent backends, we could only have one parent, and
250 * he's already been deleted.
251 *
252 * For normal backends, we need to check to see if we're in
253 * a snapshot or are the active generation for the property
254 * group. If we are, there's nothing to be done.
255 */
256 if (be == BACKEND_TYPE_NORMAL) {
257 q = backend_query_alloc();
258 backend_query_add(q,
259 "SELECT 1 "
260 "FROM pg_tbl "
261 "WHERE (pg_id = %d AND pg_gen_id = %d); "
262 "SELECT 1 "
263 "FROM snaplevel_lnk_tbl "
264 "WHERE (snaplvl_pg_id = %d AND snaplvl_gen_id = %d);",
265 ent->de_id, ent->de_gen,
266 ent->de_id, ent->de_gen);
267 r = backend_tx_run(tx, q, backend_fail_if_seen, NULL);
268 backend_query_free(q);
269
270 if (r == REP_PROTOCOL_DONE)
271 return (REP_PROTOCOL_SUCCESS); /* still in use */
272 }
273
274 info.dci_dip = dip;
275 info.dci_be = be;
276 info.dci_cb = &value_delete;
277 info.dci_result = REP_PROTOCOL_SUCCESS;
278
279 q = backend_query_alloc();
280 backend_query_add(q,
281 "SELECT DISTINCT lnk_val_id, 0 FROM prop_lnk_tbl "
282 "WHERE "
283 " (lnk_pg_id = %d AND lnk_gen_id = %d AND lnk_val_id NOTNULL); "
284 "DELETE FROM prop_lnk_tbl "
285 "WHERE (lnk_pg_id = %d AND lnk_gen_id = %d)",
286 ent->de_id, ent->de_gen, ent->de_id, ent->de_gen);
287
288 r = backend_tx_run(tx, q, push_delete_callback, &info);
289 backend_query_free(q);
290
291 if (r == REP_PROTOCOL_DONE) {
292 assert(info.dci_result != REP_PROTOCOL_SUCCESS);
293 return (info.dci_result);
294 }
295 return (r);
296 }
297
298 static int
propertygrp_delete(delete_info_t * dip,const delete_ent_t * ent)299 propertygrp_delete(delete_info_t *dip, const delete_ent_t *ent)
300 {
301 uint32_t be = ent->de_backend;
302 backend_query_t *q;
303 uint32_t gen;
304
305 int r;
306
307 backend_tx_t *tx = (be == BACKEND_TYPE_NORMAL)? dip->di_tx :
308 dip->di_np_tx;
309
310 q = backend_query_alloc();
311 backend_query_add(q,
312 "SELECT pg_gen_id FROM pg_tbl WHERE pg_id = %d; "
313 "DELETE FROM pg_tbl WHERE pg_id = %d",
314 ent->de_id, ent->de_id);
315 r = backend_tx_run_single_int(tx, q, &gen);
316 backend_query_free(q);
317
318 if (r != REP_PROTOCOL_SUCCESS)
319 return (r);
320
321 return (delete_stack_push(dip, be, &pg_lnk_tbl_delete,
322 ent->de_id, gen));
323 }
324
325 static int
snaplevel_lnk_delete(delete_info_t * dip,const delete_ent_t * ent)326 snaplevel_lnk_delete(delete_info_t *dip, const delete_ent_t *ent)
327 {
328 uint32_t be = ent->de_backend;
329 backend_query_t *q;
330 struct delete_cb_info info;
331
332 int r;
333
334 backend_tx_t *tx = (be == BACKEND_TYPE_NORMAL)? dip->di_tx :
335 dip->di_np_tx;
336
337 info.dci_dip = dip;
338 info.dci_be = be;
339 info.dci_cb = &pg_lnk_tbl_delete;
340 info.dci_result = REP_PROTOCOL_SUCCESS;
341
342 q = backend_query_alloc();
343 backend_query_add(q,
344 "SELECT snaplvl_pg_id, snaplvl_gen_id "
345 " FROM snaplevel_lnk_tbl "
346 " WHERE snaplvl_level_id = %d; "
347 "DELETE FROM snaplevel_lnk_tbl WHERE snaplvl_level_id = %d",
348 ent->de_id, ent->de_id);
349 r = backend_tx_run(tx, q, push_delete_callback, &info);
350 backend_query_free(q);
351
352 if (r == REP_PROTOCOL_DONE) {
353 assert(info.dci_result != REP_PROTOCOL_SUCCESS);
354 return (info.dci_result);
355 }
356 return (r);
357 }
358
359 static int
snaplevel_tbl_delete(delete_info_t * dip,const delete_ent_t * ent)360 snaplevel_tbl_delete(delete_info_t *dip, const delete_ent_t *ent)
361 {
362 uint32_t be = ent->de_backend;
363 backend_tx_t *tx = (be == BACKEND_TYPE_NORMAL)? dip->di_tx :
364 dip->di_np_tx;
365
366 struct delete_cb_info info;
367 backend_query_t *q;
368 int r;
369
370 assert(be == BACKEND_TYPE_NORMAL);
371
372 q = backend_query_alloc();
373 backend_query_add(q,
374 "SELECT 1 FROM snapshot_lnk_tbl WHERE lnk_snap_id = %d",
375 ent->de_id);
376 r = backend_tx_run(tx, q, backend_fail_if_seen, NULL);
377 backend_query_free(q);
378
379 if (r == REP_PROTOCOL_DONE)
380 return (REP_PROTOCOL_SUCCESS); /* still in use */
381
382 info.dci_dip = dip;
383 info.dci_be = be;
384 info.dci_cb = &snaplevel_lnk_delete;
385 info.dci_result = REP_PROTOCOL_SUCCESS;
386
387 q = backend_query_alloc();
388 backend_query_add(q,
389 "SELECT snap_level_id, 0 FROM snaplevel_tbl WHERE snap_id = %d;"
390 "DELETE FROM snaplevel_tbl WHERE snap_id = %d",
391 ent->de_id, ent->de_id);
392 r = backend_tx_run(tx, q, push_delete_callback, &info);
393 backend_query_free(q);
394
395 if (r == REP_PROTOCOL_DONE) {
396 assert(info.dci_result != REP_PROTOCOL_SUCCESS);
397 return (info.dci_result);
398 }
399 return (r);
400 }
401
402 static int
snapshot_lnk_delete(delete_info_t * dip,const delete_ent_t * ent)403 snapshot_lnk_delete(delete_info_t *dip, const delete_ent_t *ent)
404 {
405 uint32_t be = ent->de_backend;
406 backend_tx_t *tx = (be == BACKEND_TYPE_NORMAL)? dip->di_tx :
407 dip->di_np_tx;
408
409 backend_query_t *q;
410 uint32_t snapid;
411 int r;
412
413 assert(be == BACKEND_TYPE_NORMAL);
414
415 q = backend_query_alloc();
416 backend_query_add(q,
417 "SELECT lnk_snap_id FROM snapshot_lnk_tbl WHERE lnk_id = %d; "
418 "DELETE FROM snapshot_lnk_tbl WHERE lnk_id = %d",
419 ent->de_id, ent->de_id);
420 r = backend_tx_run_single_int(tx, q, &snapid);
421 backend_query_free(q);
422
423 if (r != REP_PROTOCOL_SUCCESS)
424 return (r);
425
426 return (delete_stack_push(dip, be, &snaplevel_tbl_delete, snapid, 0));
427 }
428
429 static int
pgparent_delete_add_pgs(delete_info_t * dip,uint32_t parent_id)430 pgparent_delete_add_pgs(delete_info_t *dip, uint32_t parent_id)
431 {
432 struct delete_cb_info info;
433 backend_query_t *q;
434 int r;
435
436 info.dci_dip = dip;
437 info.dci_be = BACKEND_TYPE_NORMAL;
438 info.dci_cb = &propertygrp_delete;
439 info.dci_result = REP_PROTOCOL_SUCCESS;
440
441 q = backend_query_alloc();
442 backend_query_add(q,
443 "SELECT pg_id, 0 FROM pg_tbl WHERE pg_parent_id = %d",
444 parent_id);
445
446 r = backend_tx_run(dip->di_tx, q, push_delete_callback, &info);
447
448 if (r == REP_PROTOCOL_DONE) {
449 assert(info.dci_result != REP_PROTOCOL_SUCCESS);
450 backend_query_free(q);
451 return (info.dci_result);
452 }
453 if (r != REP_PROTOCOL_SUCCESS) {
454 backend_query_free(q);
455 return (r);
456 }
457
458 if (dip->di_np_tx != NULL) {
459 info.dci_be = BACKEND_TYPE_NONPERSIST;
460
461 r = backend_tx_run(dip->di_np_tx, q, push_delete_callback,
462 &info);
463
464 if (r == REP_PROTOCOL_DONE) {
465 assert(info.dci_result != REP_PROTOCOL_SUCCESS);
466 backend_query_free(q);
467 return (info.dci_result);
468 }
469 if (r != REP_PROTOCOL_SUCCESS) {
470 backend_query_free(q);
471 return (r);
472 }
473 }
474 backend_query_free(q);
475 return (REP_PROTOCOL_SUCCESS);
476 }
477
478 static int
service_delete(delete_info_t * dip,const delete_ent_t * ent)479 service_delete(delete_info_t *dip, const delete_ent_t *ent)
480 {
481 int r;
482
483 r = backend_tx_run_update_changed(dip->di_tx,
484 "DELETE FROM service_tbl WHERE svc_id = %d", ent->de_id);
485 if (r != REP_PROTOCOL_SUCCESS)
486 return (r);
487
488 return (pgparent_delete_add_pgs(dip, ent->de_id));
489 }
490
491 static int
instance_delete(delete_info_t * dip,const delete_ent_t * ent)492 instance_delete(delete_info_t *dip, const delete_ent_t *ent)
493 {
494 struct delete_cb_info info;
495 int r;
496 backend_query_t *q;
497
498 r = backend_tx_run_update_changed(dip->di_tx,
499 "DELETE FROM instance_tbl WHERE instance_id = %d", ent->de_id);
500 if (r != REP_PROTOCOL_SUCCESS)
501 return (r);
502
503 r = pgparent_delete_add_pgs(dip, ent->de_id);
504 if (r != REP_PROTOCOL_SUCCESS)
505 return (r);
506
507 info.dci_dip = dip;
508 info.dci_be = BACKEND_TYPE_NORMAL;
509 info.dci_cb = &snapshot_lnk_delete;
510 info.dci_result = REP_PROTOCOL_SUCCESS;
511
512 q = backend_query_alloc();
513 backend_query_add(q,
514 "SELECT lnk_id, 0 FROM snapshot_lnk_tbl WHERE lnk_inst_id = %d",
515 ent->de_id);
516 r = backend_tx_run(dip->di_tx, q, push_delete_callback, &info);
517 backend_query_free(q);
518
519 if (r == REP_PROTOCOL_DONE) {
520 assert(info.dci_result != REP_PROTOCOL_SUCCESS);
521 return (info.dci_result);
522 }
523 return (r);
524 }
525
526 /*ARGSUSED*/
527 static int
fill_child_callback(void * data,int columns,char ** vals,char ** names)528 fill_child_callback(void *data, int columns, char **vals, char **names)
529 {
530 child_info_t *cp = data;
531 rc_node_t *np;
532 uint32_t main_id;
533 const char *name;
534 const char *cur;
535 rc_node_lookup_t *lp = &cp->ci_base_nl;
536
537 assert(columns == 2);
538
539 name = *vals++;
540 columns--;
541
542 cur = *vals++;
543 columns--;
544 string_to_id(cur, &main_id, "id");
545
546 lp->rl_main_id = main_id;
547
548 if ((np = rc_node_alloc()) == NULL)
549 return (BACKEND_CALLBACK_ABORT);
550
551 np = rc_node_setup(np, lp, name, cp->ci_parent);
552 rc_node_rele(np);
553
554 return (BACKEND_CALLBACK_CONTINUE);
555 }
556
557 /*ARGSUSED*/
558 static int
fill_snapshot_callback(void * data,int columns,char ** vals,char ** names)559 fill_snapshot_callback(void *data, int columns, char **vals, char **names)
560 {
561 child_info_t *cp = data;
562 rc_node_t *np;
563 uint32_t main_id;
564 uint32_t snap_id;
565 const char *name;
566 const char *cur;
567 const char *snap;
568 rc_node_lookup_t *lp = &cp->ci_base_nl;
569
570 assert(columns == 3);
571
572 name = *vals++;
573 columns--;
574
575 cur = *vals++;
576 columns--;
577 snap = *vals++;
578 columns--;
579
580 string_to_id(cur, &main_id, "lnk_id");
581 string_to_id(snap, &snap_id, "lnk_snap_id");
582
583 lp->rl_main_id = main_id;
584
585 if ((np = rc_node_alloc()) == NULL)
586 return (BACKEND_CALLBACK_ABORT);
587
588 np = rc_node_setup_snapshot(np, lp, name, snap_id, cp->ci_parent);
589 rc_node_rele(np);
590
591 return (BACKEND_CALLBACK_CONTINUE);
592 }
593
594 /*ARGSUSED*/
595 static int
fill_pg_callback(void * data,int columns,char ** vals,char ** names)596 fill_pg_callback(void *data, int columns, char **vals, char **names)
597 {
598 child_info_t *cip = data;
599 const char *name;
600 const char *type;
601 const char *cur;
602 uint32_t main_id;
603 uint32_t flags;
604 uint32_t gen_id;
605
606 rc_node_lookup_t *lp = &cip->ci_base_nl;
607 rc_node_t *newnode, *pg;
608
609 assert(columns == 5);
610
611 name = *vals++; /* pg_name */
612 columns--;
613
614 cur = *vals++; /* pg_id */
615 columns--;
616 string_to_id(cur, &main_id, "pg_id");
617
618 lp->rl_main_id = main_id;
619
620 cur = *vals++; /* pg_gen_id */
621 columns--;
622 string_to_id(cur, &gen_id, "pg_gen_id");
623
624 type = *vals++; /* pg_type */
625 columns--;
626
627 cur = *vals++; /* pg_flags */
628 columns--;
629 string_to_id(cur, &flags, "pg_flags");
630
631 if ((newnode = rc_node_alloc()) == NULL)
632 return (BACKEND_CALLBACK_ABORT);
633
634 pg = rc_node_setup_pg(newnode, lp, name, type, flags, gen_id,
635 cip->ci_parent);
636 if (pg == NULL) {
637 rc_node_destroy(newnode);
638 return (BACKEND_CALLBACK_ABORT);
639 }
640
641 rc_node_rele(pg);
642
643 return (BACKEND_CALLBACK_CONTINUE);
644 }
645
646 struct property_value_info {
647 char *pvi_base;
648 size_t pvi_pos;
649 size_t pvi_size;
650 size_t pvi_count;
651 };
652
653 /*ARGSUSED*/
654 static int
property_value_size_cb(void * data,int columns,char ** vals,char ** names)655 property_value_size_cb(void *data, int columns, char **vals, char **names)
656 {
657 struct property_value_info *info = data;
658 assert(columns == 1);
659
660 info->pvi_size += strlen(vals[0]) + 1; /* count the '\0' */
661
662 return (BACKEND_CALLBACK_CONTINUE);
663 }
664
665 /*ARGSUSED*/
666 static int
property_value_cb(void * data,int columns,char ** vals,char ** names)667 property_value_cb(void *data, int columns, char **vals, char **names)
668 {
669 struct property_value_info *info = data;
670 size_t pos, left, len;
671
672 assert(columns == 1);
673 pos = info->pvi_pos;
674 left = info->pvi_size - pos;
675
676 pos = info->pvi_pos;
677 left = info->pvi_size - pos;
678
679 if ((len = strlcpy(&info->pvi_base[pos], vals[0], left)) >= left) {
680 /*
681 * since we preallocated, above, this shouldn't happen
682 */
683 backend_panic("unexpected database change");
684 }
685
686 len += 1; /* count the '\0' */
687
688 info->pvi_pos += len;
689 info->pvi_count++;
690
691 return (BACKEND_CALLBACK_CONTINUE);
692 }
693
694 /*ARGSUSED*/
695 void
object_free_values(const char * vals,uint32_t type,size_t count,size_t size)696 object_free_values(const char *vals, uint32_t type, size_t count, size_t size)
697 {
698 if (vals != NULL)
699 uu_free((void *)vals);
700 }
701
702 /*ARGSUSED*/
703 static int
fill_property_callback(void * data,int columns,char ** vals,char ** names)704 fill_property_callback(void *data, int columns, char **vals, char **names)
705 {
706 child_info_t *cp = data;
707 backend_tx_t *tx = cp->ci_tx;
708 uint32_t main_id;
709 const char *name;
710 const char *cur;
711 rep_protocol_value_type_t type;
712 rc_node_lookup_t *lp = &cp->ci_base_nl;
713 struct property_value_info info;
714 int rc;
715
716 assert(columns == 4);
717 assert(tx != NULL);
718
719 info.pvi_base = NULL;
720 info.pvi_pos = 0;
721 info.pvi_size = 0;
722 info.pvi_count = 0;
723
724 name = *vals++;
725
726 cur = *vals++;
727 string_to_id(cur, &main_id, "lnk_prop_id");
728
729 cur = *vals++;
730 assert(('a' <= cur[0] && 'z' >= cur[0]) ||
731 ('A' <= cur[0] && 'Z' >= cur[0]) &&
732 (cur[1] == 0 || ('a' <= cur[1] && 'z' >= cur[1]) ||
733 ('A' <= cur[1] && 'Z' >= cur[1])));
734 type = cur[0] | (cur[1] << 8);
735
736 lp->rl_main_id = main_id;
737
738 /*
739 * fill in the values, if any
740 */
741 if ((cur = *vals++) != NULL) {
742 rep_protocol_responseid_t r;
743 backend_query_t *q = backend_query_alloc();
744
745 /*
746 * Ensure that select operation is reflective
747 * of repository schema. If the repository has
748 * been upgraded, make use of value ordering
749 * by retrieving values in order using the
750 * value_order column. Otherwise, simply
751 * run the select with no order specified.
752 * The order-insensitive select is necessary
753 * as on first reboot post-upgrade, the repository
754 * contents need to be read before the repository
755 * backend is writable (and upgrade is possible).
756 */
757 if (backend_is_upgraded(tx)) {
758 backend_query_add(q,
759 "SELECT value_value FROM value_tbl "
760 "WHERE (value_id = '%q') ORDER BY value_order",
761 cur);
762 } else {
763 backend_query_add(q,
764 "SELECT value_value FROM value_tbl "
765 "WHERE (value_id = '%q')",
766 cur);
767 }
768
769 switch (r = backend_tx_run(tx, q, property_value_size_cb,
770 &info)) {
771 case REP_PROTOCOL_SUCCESS:
772 break;
773
774 case REP_PROTOCOL_FAIL_NO_RESOURCES:
775 backend_query_free(q);
776 return (BACKEND_CALLBACK_ABORT);
777
778 case REP_PROTOCOL_DONE:
779 default:
780 backend_panic("backend_tx_run() returned %d", r);
781 }
782 if (info.pvi_size > 0) {
783 info.pvi_base = uu_zalloc(info.pvi_size);
784 if (info.pvi_base == NULL) {
785 backend_query_free(q);
786 return (BACKEND_CALLBACK_ABORT);
787 }
788 switch (r = backend_tx_run(tx, q, property_value_cb,
789 &info)) {
790 case REP_PROTOCOL_SUCCESS:
791 break;
792
793 case REP_PROTOCOL_FAIL_NO_RESOURCES:
794 uu_free(info.pvi_base);
795 backend_query_free(q);
796 return (BACKEND_CALLBACK_ABORT);
797
798 case REP_PROTOCOL_DONE:
799 default:
800 backend_panic("backend_tx_run() returned %d",
801 r);
802 }
803 }
804 backend_query_free(q);
805 }
806
807 rc = rc_node_create_property(cp->ci_parent, lp, name, type,
808 info.pvi_base, info.pvi_count, info.pvi_size);
809 if (rc != REP_PROTOCOL_SUCCESS) {
810 assert(rc == REP_PROTOCOL_FAIL_NO_RESOURCES);
811 return (BACKEND_CALLBACK_ABORT);
812 }
813
814 return (BACKEND_CALLBACK_CONTINUE);
815 }
816
817 /*
818 * The *_setup_child_info() functions fill in a child_info_t structure with the
819 * information for the children of np with type type.
820 *
821 * They fail with
822 * _TYPE_MISMATCH - object cannot have children of type type
823 */
824
825 static int
scope_setup_child_info(rc_node_t * np,uint32_t type,child_info_t * cip)826 scope_setup_child_info(rc_node_t *np, uint32_t type, child_info_t *cip)
827 {
828 if (type != REP_PROTOCOL_ENTITY_SERVICE)
829 return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
830
831 bzero(cip, sizeof (*cip));
832 cip->ci_parent = np;
833 cip->ci_base_nl.rl_type = type;
834 cip->ci_base_nl.rl_backend = np->rn_id.rl_backend;
835 return (REP_PROTOCOL_SUCCESS);
836 }
837
838 static int
service_setup_child_info(rc_node_t * np,uint32_t type,child_info_t * cip)839 service_setup_child_info(rc_node_t *np, uint32_t type, child_info_t *cip)
840 {
841 switch (type) {
842 case REP_PROTOCOL_ENTITY_INSTANCE:
843 case REP_PROTOCOL_ENTITY_PROPERTYGRP:
844 break;
845 default:
846 return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
847 }
848
849 bzero(cip, sizeof (*cip));
850 cip->ci_parent = np;
851 cip->ci_base_nl.rl_type = type;
852 cip->ci_base_nl.rl_backend = np->rn_id.rl_backend;
853 cip->ci_base_nl.rl_ids[ID_SERVICE] = np->rn_id.rl_main_id;
854
855 return (REP_PROTOCOL_SUCCESS);
856 }
857
858 static int
instance_setup_child_info(rc_node_t * np,uint32_t type,child_info_t * cip)859 instance_setup_child_info(rc_node_t *np, uint32_t type, child_info_t *cip)
860 {
861 switch (type) {
862 case REP_PROTOCOL_ENTITY_PROPERTYGRP:
863 case REP_PROTOCOL_ENTITY_SNAPSHOT:
864 break;
865 default:
866 return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
867 }
868
869 bzero(cip, sizeof (*cip));
870 cip->ci_parent = np;
871 cip->ci_base_nl.rl_type = type;
872 cip->ci_base_nl.rl_backend = np->rn_id.rl_backend;
873 cip->ci_base_nl.rl_ids[ID_SERVICE] = np->rn_id.rl_ids[ID_SERVICE];
874 cip->ci_base_nl.rl_ids[ID_INSTANCE] = np->rn_id.rl_main_id;
875
876 return (REP_PROTOCOL_SUCCESS);
877 }
878
879 static int
snaplevel_setup_child_info(rc_node_t * np,uint32_t type,child_info_t * cip)880 snaplevel_setup_child_info(rc_node_t *np, uint32_t type, child_info_t *cip)
881 {
882 if (type != REP_PROTOCOL_ENTITY_PROPERTYGRP)
883 return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
884
885 bzero(cip, sizeof (*cip));
886 cip->ci_parent = np;
887 cip->ci_base_nl.rl_type = type;
888 cip->ci_base_nl.rl_backend = np->rn_id.rl_backend;
889 cip->ci_base_nl.rl_ids[ID_SERVICE] = np->rn_id.rl_ids[ID_SERVICE];
890 cip->ci_base_nl.rl_ids[ID_INSTANCE] = np->rn_id.rl_ids[ID_INSTANCE];
891 cip->ci_base_nl.rl_ids[ID_NAME] = np->rn_id.rl_ids[ID_NAME];
892 cip->ci_base_nl.rl_ids[ID_SNAPSHOT] = np->rn_id.rl_ids[ID_SNAPSHOT];
893 cip->ci_base_nl.rl_ids[ID_LEVEL] = np->rn_id.rl_main_id;
894
895 return (REP_PROTOCOL_SUCCESS);
896 }
897
898 static int
propertygrp_setup_child_info(rc_node_t * pg,uint32_t type,child_info_t * cip)899 propertygrp_setup_child_info(rc_node_t *pg, uint32_t type, child_info_t *cip)
900 {
901 if (type != REP_PROTOCOL_ENTITY_PROPERTY)
902 return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
903
904 bzero(cip, sizeof (*cip));
905 cip->ci_parent = pg;
906 cip->ci_base_nl.rl_type = type;
907 cip->ci_base_nl.rl_backend = pg->rn_id.rl_backend;
908 cip->ci_base_nl.rl_ids[ID_SERVICE] = pg->rn_id.rl_ids[ID_SERVICE];
909 cip->ci_base_nl.rl_ids[ID_INSTANCE] = pg->rn_id.rl_ids[ID_INSTANCE];
910 cip->ci_base_nl.rl_ids[ID_PG] = pg->rn_id.rl_main_id;
911 cip->ci_base_nl.rl_ids[ID_GEN] = pg->rn_gen_id;
912 cip->ci_base_nl.rl_ids[ID_NAME] = pg->rn_id.rl_ids[ID_NAME];
913 cip->ci_base_nl.rl_ids[ID_SNAPSHOT] = pg->rn_id.rl_ids[ID_SNAPSHOT];
914 cip->ci_base_nl.rl_ids[ID_LEVEL] = pg->rn_id.rl_ids[ID_LEVEL];
915
916 return (REP_PROTOCOL_SUCCESS);
917 }
918
919 /*
920 * The *_fill_children() functions populate the children of the given rc_node_t
921 * by querying the database and calling rc_node_setup_*() functions (usually
922 * via a fill_*_callback()).
923 *
924 * They fail with
925 * _NO_RESOURCES
926 */
927
928 /*
929 * Returns
930 * _NO_RESOURCES
931 * _SUCCESS
932 */
933 static int
scope_fill_children(rc_node_t * np)934 scope_fill_children(rc_node_t *np)
935 {
936 backend_query_t *q;
937 child_info_t ci;
938 int res;
939
940 (void) scope_setup_child_info(np, REP_PROTOCOL_ENTITY_SERVICE, &ci);
941
942 q = backend_query_alloc();
943 backend_query_append(q, "SELECT svc_name, svc_id FROM service_tbl");
944 res = backend_run(BACKEND_TYPE_NORMAL, q, fill_child_callback, &ci);
945 backend_query_free(q);
946
947 if (res == REP_PROTOCOL_DONE)
948 res = REP_PROTOCOL_FAIL_NO_RESOURCES;
949 return (res);
950 }
951
952 /*
953 * Returns
954 * _NO_RESOURCES
955 * _SUCCESS
956 */
957 static int
service_fill_children(rc_node_t * np)958 service_fill_children(rc_node_t *np)
959 {
960 backend_query_t *q;
961 child_info_t ci;
962 int res;
963
964 assert(np->rn_id.rl_backend == BACKEND_TYPE_NORMAL);
965
966 (void) service_setup_child_info(np, REP_PROTOCOL_ENTITY_INSTANCE, &ci);
967
968 q = backend_query_alloc();
969 backend_query_add(q,
970 "SELECT instance_name, instance_id FROM instance_tbl"
971 " WHERE (instance_svc = %d)",
972 np->rn_id.rl_main_id);
973 res = backend_run(BACKEND_TYPE_NORMAL, q, fill_child_callback, &ci);
974 backend_query_free(q);
975
976 if (res == REP_PROTOCOL_DONE)
977 res = REP_PROTOCOL_FAIL_NO_RESOURCES;
978 if (res != REP_PROTOCOL_SUCCESS)
979 return (res);
980
981 (void) service_setup_child_info(np, REP_PROTOCOL_ENTITY_PROPERTYGRP,
982 &ci);
983
984 q = backend_query_alloc();
985 backend_query_add(q,
986 "SELECT pg_name, pg_id, pg_gen_id, pg_type, pg_flags FROM pg_tbl"
987 " WHERE (pg_parent_id = %d)",
988 np->rn_id.rl_main_id);
989
990 ci.ci_base_nl.rl_backend = BACKEND_TYPE_NORMAL;
991 res = backend_run(BACKEND_TYPE_NORMAL, q, fill_pg_callback, &ci);
992 if (res == REP_PROTOCOL_SUCCESS) {
993 ci.ci_base_nl.rl_backend = BACKEND_TYPE_NONPERSIST;
994 res = backend_run(BACKEND_TYPE_NONPERSIST, q,
995 fill_pg_callback, &ci);
996 /* nonpersistant database may not exist */
997 if (res == REP_PROTOCOL_FAIL_BACKEND_ACCESS)
998 res = REP_PROTOCOL_SUCCESS;
999 }
1000 if (res == REP_PROTOCOL_DONE)
1001 res = REP_PROTOCOL_FAIL_NO_RESOURCES;
1002 backend_query_free(q);
1003
1004 return (res);
1005 }
1006
1007 /*
1008 * Returns
1009 * _NO_RESOURCES
1010 * _SUCCESS
1011 */
1012 static int
instance_fill_children(rc_node_t * np)1013 instance_fill_children(rc_node_t *np)
1014 {
1015 backend_query_t *q;
1016 child_info_t ci;
1017 int res;
1018
1019 assert(np->rn_id.rl_backend == BACKEND_TYPE_NORMAL);
1020
1021 /* Get child property groups */
1022 (void) instance_setup_child_info(np, REP_PROTOCOL_ENTITY_PROPERTYGRP,
1023 &ci);
1024
1025 q = backend_query_alloc();
1026 backend_query_add(q,
1027 "SELECT pg_name, pg_id, pg_gen_id, pg_type, pg_flags FROM pg_tbl"
1028 " WHERE (pg_parent_id = %d)",
1029 np->rn_id.rl_main_id);
1030 ci.ci_base_nl.rl_backend = BACKEND_TYPE_NORMAL;
1031 res = backend_run(BACKEND_TYPE_NORMAL, q, fill_pg_callback, &ci);
1032 if (res == REP_PROTOCOL_SUCCESS) {
1033 ci.ci_base_nl.rl_backend = BACKEND_TYPE_NONPERSIST;
1034 res = backend_run(BACKEND_TYPE_NONPERSIST, q,
1035 fill_pg_callback, &ci);
1036 /* nonpersistant database may not exist */
1037 if (res == REP_PROTOCOL_FAIL_BACKEND_ACCESS)
1038 res = REP_PROTOCOL_SUCCESS;
1039 }
1040 if (res == REP_PROTOCOL_DONE)
1041 res = REP_PROTOCOL_FAIL_NO_RESOURCES;
1042 backend_query_free(q);
1043
1044 if (res != REP_PROTOCOL_SUCCESS)
1045 return (res);
1046
1047 /* Get child snapshots */
1048 (void) instance_setup_child_info(np, REP_PROTOCOL_ENTITY_SNAPSHOT,
1049 &ci);
1050
1051 q = backend_query_alloc();
1052 backend_query_add(q,
1053 "SELECT lnk_snap_name, lnk_id, lnk_snap_id FROM snapshot_lnk_tbl"
1054 " WHERE (lnk_inst_id = %d)",
1055 np->rn_id.rl_main_id);
1056 res = backend_run(BACKEND_TYPE_NORMAL, q, fill_snapshot_callback, &ci);
1057 if (res == REP_PROTOCOL_DONE)
1058 res = REP_PROTOCOL_FAIL_NO_RESOURCES;
1059 backend_query_free(q);
1060
1061 return (res);
1062 }
1063
1064 /*
1065 * Returns
1066 * _NO_RESOURCES
1067 * _SUCCESS
1068 */
1069 static int
snapshot_fill_children(rc_node_t * np)1070 snapshot_fill_children(rc_node_t *np)
1071 {
1072 rc_node_t *nnp;
1073 rc_snapshot_t *sp, *oldsp;
1074 rc_snaplevel_t *lvl;
1075 rc_node_lookup_t nl;
1076 int r;
1077
1078 /* Get the rc_snapshot_t (& its rc_snaplevel_t's). */
1079 (void) pthread_mutex_lock(&np->rn_lock);
1080 sp = np->rn_snapshot;
1081 (void) pthread_mutex_unlock(&np->rn_lock);
1082 if (sp == NULL) {
1083 r = rc_snapshot_get(np->rn_snapshot_id, &sp);
1084 if (r != REP_PROTOCOL_SUCCESS) {
1085 assert(r == REP_PROTOCOL_FAIL_NO_RESOURCES);
1086 return (r);
1087 }
1088 (void) pthread_mutex_lock(&np->rn_lock);
1089 oldsp = np->rn_snapshot;
1090 assert(oldsp == NULL || oldsp == sp);
1091 np->rn_snapshot = sp;
1092 (void) pthread_mutex_unlock(&np->rn_lock);
1093 if (oldsp != NULL)
1094 rc_snapshot_rele(oldsp);
1095 }
1096
1097 bzero(&nl, sizeof (nl));
1098 nl.rl_type = REP_PROTOCOL_ENTITY_SNAPLEVEL;
1099 nl.rl_backend = np->rn_id.rl_backend;
1100 nl.rl_ids[ID_SERVICE] = np->rn_id.rl_ids[ID_SERVICE];
1101 nl.rl_ids[ID_INSTANCE] = np->rn_id.rl_ids[ID_INSTANCE];
1102 nl.rl_ids[ID_NAME] = np->rn_id.rl_main_id;
1103 nl.rl_ids[ID_SNAPSHOT] = np->rn_snapshot_id;
1104
1105 /* Create rc_node_t's for the snapshot's rc_snaplevel_t's. */
1106 for (lvl = sp->rs_levels; lvl != NULL; lvl = lvl->rsl_next) {
1107 nnp = rc_node_alloc();
1108 assert(nnp != NULL);
1109 nl.rl_main_id = lvl->rsl_level_id;
1110 nnp = rc_node_setup_snaplevel(nnp, &nl, lvl, np);
1111 rc_node_rele(nnp);
1112 }
1113
1114 return (REP_PROTOCOL_SUCCESS);
1115 }
1116
1117 /*
1118 * Returns
1119 * _NO_RESOURCES
1120 * _SUCCESS
1121 */
1122 static int
snaplevel_fill_children(rc_node_t * np)1123 snaplevel_fill_children(rc_node_t *np)
1124 {
1125 rc_snaplevel_t *lvl = np->rn_snaplevel;
1126 child_info_t ci;
1127 int res;
1128 backend_query_t *q;
1129
1130 (void) snaplevel_setup_child_info(np, REP_PROTOCOL_ENTITY_PROPERTYGRP,
1131 &ci);
1132
1133 q = backend_query_alloc();
1134 backend_query_add(q,
1135 "SELECT snaplvl_pg_name, snaplvl_pg_id, snaplvl_gen_id, "
1136 " snaplvl_pg_type, snaplvl_pg_flags "
1137 " FROM snaplevel_lnk_tbl "
1138 " WHERE (snaplvl_level_id = %d)",
1139 lvl->rsl_level_id);
1140 res = backend_run(BACKEND_TYPE_NORMAL, q, fill_pg_callback, &ci);
1141 if (res == REP_PROTOCOL_DONE)
1142 res = REP_PROTOCOL_FAIL_NO_RESOURCES;
1143 backend_query_free(q);
1144
1145 return (res);
1146 }
1147
1148 /*
1149 * Returns
1150 * _NO_RESOURCES
1151 * _SUCCESS
1152 */
1153 static int
propertygrp_fill_children(rc_node_t * np)1154 propertygrp_fill_children(rc_node_t *np)
1155 {
1156 backend_query_t *q;
1157 child_info_t ci;
1158 int res;
1159 backend_tx_t *tx;
1160
1161 backend_type_t backend = np->rn_id.rl_backend;
1162
1163 (void) propertygrp_setup_child_info(np, REP_PROTOCOL_ENTITY_PROPERTY,
1164 &ci);
1165
1166 res = backend_tx_begin_ro(backend, &tx);
1167 if (res != REP_PROTOCOL_SUCCESS) {
1168 /*
1169 * If the backend didn't exist, we wouldn't have got this
1170 * property group.
1171 */
1172 assert(res != REP_PROTOCOL_FAIL_BACKEND_ACCESS);
1173 return (res);
1174 }
1175
1176 ci.ci_tx = tx;
1177
1178 q = backend_query_alloc();
1179 backend_query_add(q,
1180 "SELECT lnk_prop_name, lnk_prop_id, lnk_prop_type, lnk_val_id "
1181 "FROM prop_lnk_tbl "
1182 "WHERE (lnk_pg_id = %d AND lnk_gen_id = %d)",
1183 np->rn_id.rl_main_id, np->rn_gen_id);
1184 res = backend_tx_run(tx, q, fill_property_callback, &ci);
1185 if (res == REP_PROTOCOL_DONE)
1186 res = REP_PROTOCOL_FAIL_NO_RESOURCES;
1187 backend_query_free(q);
1188 backend_tx_end_ro(tx);
1189
1190 return (res);
1191 }
1192
1193 /*
1194 * Fails with
1195 * _TYPE_MISMATCH - lp is not for a service
1196 * _INVALID_TYPE - lp has invalid type
1197 * _BAD_REQUEST - name is invalid
1198 */
1199 static int
scope_query_child(backend_query_t * q,rc_node_lookup_t * lp,const char * name)1200 scope_query_child(backend_query_t *q, rc_node_lookup_t *lp, const char *name)
1201 {
1202 uint32_t type = lp->rl_type;
1203 int rc;
1204
1205 if (type != REP_PROTOCOL_ENTITY_SERVICE)
1206 return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
1207
1208 if ((rc = rc_check_type_name(type, name)) != REP_PROTOCOL_SUCCESS)
1209 return (rc);
1210
1211 backend_query_add(q,
1212 "SELECT svc_id FROM service_tbl "
1213 "WHERE svc_name = '%q'",
1214 name);
1215
1216 return (REP_PROTOCOL_SUCCESS);
1217 }
1218
1219 /*
1220 * Fails with
1221 * _NO_RESOURCES - out of memory
1222 */
1223 static int
scope_insert_child(backend_tx_t * tx,rc_node_lookup_t * lp,const char * name)1224 scope_insert_child(backend_tx_t *tx, rc_node_lookup_t *lp, const char *name)
1225 {
1226 return (backend_tx_run_update(tx,
1227 "INSERT INTO service_tbl (svc_id, svc_name) "
1228 "VALUES (%d, '%q')",
1229 lp->rl_main_id, name));
1230 }
1231
1232 /*
1233 * Fails with
1234 * _TYPE_MISMATCH - lp is not for an instance or property group
1235 * _INVALID_TYPE - lp has invalid type
1236 * _BAD_REQUEST - name is invalid
1237 */
1238 static int
service_query_child(backend_query_t * q,rc_node_lookup_t * lp,const char * name)1239 service_query_child(backend_query_t *q, rc_node_lookup_t *lp, const char *name)
1240 {
1241 uint32_t type = lp->rl_type;
1242 int rc;
1243
1244 if (type != REP_PROTOCOL_ENTITY_INSTANCE &&
1245 type != REP_PROTOCOL_ENTITY_PROPERTYGRP)
1246 return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
1247
1248 if ((rc = rc_check_type_name(type, name)) != REP_PROTOCOL_SUCCESS)
1249 return (rc);
1250
1251 switch (type) {
1252 case REP_PROTOCOL_ENTITY_INSTANCE:
1253 backend_query_add(q,
1254 "SELECT instance_id FROM instance_tbl "
1255 "WHERE instance_name = '%q' AND instance_svc = %d",
1256 name, lp->rl_ids[ID_SERVICE]);
1257 break;
1258 case REP_PROTOCOL_ENTITY_PROPERTYGRP:
1259 backend_query_add(q,
1260 "SELECT pg_id FROM pg_tbl "
1261 " WHERE pg_name = '%q' AND pg_parent_id = %d",
1262 name, lp->rl_ids[ID_SERVICE]);
1263 break;
1264 default:
1265 assert(0);
1266 abort();
1267 }
1268
1269 return (REP_PROTOCOL_SUCCESS);
1270 }
1271
1272 /*
1273 * Fails with
1274 * _NO_RESOURCES - out of memory
1275 */
1276 static int
service_insert_child(backend_tx_t * tx,rc_node_lookup_t * lp,const char * name)1277 service_insert_child(backend_tx_t *tx, rc_node_lookup_t *lp, const char *name)
1278 {
1279 return (backend_tx_run_update(tx,
1280 "INSERT INTO instance_tbl "
1281 " (instance_id, instance_name, instance_svc) "
1282 "VALUES (%d, '%q', %d)",
1283 lp->rl_main_id, name, lp->rl_ids[ID_SERVICE]));
1284 }
1285
1286 /*
1287 * Fails with
1288 * _NO_RESOURCES - out of memory
1289 */
1290 static int
instance_insert_child(backend_tx_t * tx,rc_node_lookup_t * lp,const char * name)1291 instance_insert_child(backend_tx_t *tx, rc_node_lookup_t *lp, const char *name)
1292 {
1293 return (backend_tx_run_update(tx,
1294 "INSERT INTO snapshot_lnk_tbl "
1295 " (lnk_id, lnk_inst_id, lnk_snap_name, lnk_snap_id) "
1296 "VALUES (%d, %d, '%q', 0)",
1297 lp->rl_main_id, lp->rl_ids[ID_INSTANCE], name));
1298 }
1299
1300 /*
1301 * Fails with
1302 * _TYPE_MISMATCH - lp is not for a property group or snapshot
1303 * _INVALID_TYPE - lp has invalid type
1304 * _BAD_REQUEST - name is invalid
1305 */
1306 static int
instance_query_child(backend_query_t * q,rc_node_lookup_t * lp,const char * name)1307 instance_query_child(backend_query_t *q, rc_node_lookup_t *lp, const char *name)
1308 {
1309 uint32_t type = lp->rl_type;
1310 int rc;
1311
1312 if (type != REP_PROTOCOL_ENTITY_PROPERTYGRP &&
1313 type != REP_PROTOCOL_ENTITY_SNAPSHOT)
1314 return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
1315
1316 if ((rc = rc_check_type_name(type, name)) != REP_PROTOCOL_SUCCESS)
1317 return (rc);
1318
1319 switch (type) {
1320 case REP_PROTOCOL_ENTITY_PROPERTYGRP:
1321 backend_query_add(q,
1322 "SELECT pg_id FROM pg_tbl "
1323 " WHERE pg_name = '%q' AND pg_parent_id = %d",
1324 name, lp->rl_ids[ID_INSTANCE]);
1325 break;
1326 case REP_PROTOCOL_ENTITY_SNAPSHOT:
1327 backend_query_add(q,
1328 "SELECT lnk_id FROM snapshot_lnk_tbl "
1329 " WHERE lnk_snap_name = '%q' AND lnk_inst_id = %d",
1330 name, lp->rl_ids[ID_INSTANCE]);
1331 break;
1332 default:
1333 assert(0);
1334 abort();
1335 }
1336
1337 return (REP_PROTOCOL_SUCCESS);
1338 }
1339
1340 static int
generic_insert_pg_child(backend_tx_t * tx,rc_node_lookup_t * lp,const char * name,const char * pgtype,uint32_t flags,uint32_t gen)1341 generic_insert_pg_child(backend_tx_t *tx, rc_node_lookup_t *lp,
1342 const char *name, const char *pgtype, uint32_t flags, uint32_t gen)
1343 {
1344 int parent_id = (lp->rl_ids[ID_INSTANCE] != 0)?
1345 lp->rl_ids[ID_INSTANCE] : lp->rl_ids[ID_SERVICE];
1346 return (backend_tx_run_update(tx,
1347 "INSERT INTO pg_tbl "
1348 " (pg_id, pg_name, pg_parent_id, pg_type, pg_flags, pg_gen_id) "
1349 "VALUES (%d, '%q', %d, '%q', %d, %d)",
1350 lp->rl_main_id, name, parent_id, pgtype, flags, gen));
1351 }
1352
1353 static int
service_delete_start(rc_node_t * np,delete_info_t * dip)1354 service_delete_start(rc_node_t *np, delete_info_t *dip)
1355 {
1356 int r;
1357 backend_query_t *q = backend_query_alloc();
1358
1359 /*
1360 * Check for child instances, and refuse to delete if they exist.
1361 */
1362 backend_query_add(q,
1363 "SELECT 1 FROM instance_tbl WHERE instance_svc = %d",
1364 np->rn_id.rl_main_id);
1365
1366 r = backend_tx_run(dip->di_tx, q, backend_fail_if_seen, NULL);
1367 backend_query_free(q);
1368
1369 if (r == REP_PROTOCOL_DONE)
1370 return (REP_PROTOCOL_FAIL_EXISTS); /* instances exist */
1371
1372 return (delete_stack_push(dip, BACKEND_TYPE_NORMAL, &service_delete,
1373 np->rn_id.rl_main_id, 0));
1374 }
1375
1376 static int
instance_delete_start(rc_node_t * np,delete_info_t * dip)1377 instance_delete_start(rc_node_t *np, delete_info_t *dip)
1378 {
1379 return (delete_stack_push(dip, BACKEND_TYPE_NORMAL, &instance_delete,
1380 np->rn_id.rl_main_id, 0));
1381 }
1382
1383 static int
snapshot_delete_start(rc_node_t * np,delete_info_t * dip)1384 snapshot_delete_start(rc_node_t *np, delete_info_t *dip)
1385 {
1386 return (delete_stack_push(dip, BACKEND_TYPE_NORMAL,
1387 &snapshot_lnk_delete, np->rn_id.rl_main_id, 0));
1388 }
1389
1390 static int
propertygrp_delete_start(rc_node_t * np,delete_info_t * dip)1391 propertygrp_delete_start(rc_node_t *np, delete_info_t *dip)
1392 {
1393 return (delete_stack_push(dip, np->rn_id.rl_backend,
1394 &propertygrp_delete, np->rn_id.rl_main_id, 0));
1395 }
1396
1397 static object_info_t info[] = {
1398 {REP_PROTOCOL_ENTITY_NONE},
1399 {REP_PROTOCOL_ENTITY_SCOPE,
1400 BACKEND_ID_INVALID,
1401 scope_fill_children,
1402 scope_setup_child_info,
1403 scope_query_child,
1404 scope_insert_child,
1405 NULL,
1406 NULL,
1407 },
1408 {REP_PROTOCOL_ENTITY_SERVICE,
1409 BACKEND_ID_SERVICE_INSTANCE,
1410 service_fill_children,
1411 service_setup_child_info,
1412 service_query_child,
1413 service_insert_child,
1414 generic_insert_pg_child,
1415 service_delete_start,
1416 },
1417 {REP_PROTOCOL_ENTITY_INSTANCE,
1418 BACKEND_ID_SERVICE_INSTANCE,
1419 instance_fill_children,
1420 instance_setup_child_info,
1421 instance_query_child,
1422 instance_insert_child,
1423 generic_insert_pg_child,
1424 instance_delete_start,
1425 },
1426 {REP_PROTOCOL_ENTITY_SNAPSHOT,
1427 BACKEND_ID_SNAPNAME,
1428 snapshot_fill_children,
1429 NULL,
1430 NULL,
1431 NULL,
1432 NULL,
1433 snapshot_delete_start,
1434 },
1435 {REP_PROTOCOL_ENTITY_SNAPLEVEL,
1436 BACKEND_ID_SNAPLEVEL,
1437 snaplevel_fill_children,
1438 snaplevel_setup_child_info,
1439 },
1440 {REP_PROTOCOL_ENTITY_PROPERTYGRP,
1441 BACKEND_ID_PROPERTYGRP,
1442 propertygrp_fill_children,
1443 NULL,
1444 NULL,
1445 NULL,
1446 NULL,
1447 propertygrp_delete_start,
1448 },
1449 {REP_PROTOCOL_ENTITY_PROPERTY},
1450 {-1UL}
1451 };
1452 #define NUM_INFO (sizeof (info) / sizeof (*info))
1453
1454 /*
1455 * object_fill_children() populates the child list of an rc_node_t by calling
1456 * the appropriate <type>_fill_children() which runs backend queries that
1457 * call an appropriate fill_*_callback() which takes a row of results,
1458 * decodes them, and calls an rc_node_setup*() function in rc_node.c to create
1459 * a child.
1460 *
1461 * Fails with
1462 * _NO_RESOURCES
1463 */
1464 int
object_fill_children(rc_node_t * pp)1465 object_fill_children(rc_node_t *pp)
1466 {
1467 uint32_t type = pp->rn_id.rl_type;
1468 assert(type > 0 && type < NUM_INFO);
1469
1470 return ((*info[type].obj_fill_children)(pp));
1471 }
1472
1473 int
object_delete(rc_node_t * pp)1474 object_delete(rc_node_t *pp)
1475 {
1476 int rc;
1477
1478 delete_info_t dip;
1479 delete_ent_t de;
1480
1481 uint32_t type = pp->rn_id.rl_type;
1482 assert(type > 0 && type < NUM_INFO);
1483
1484 if (info[type].obj_delete_start == NULL)
1485 return (REP_PROTOCOL_FAIL_BAD_REQUEST);
1486
1487 (void) memset(&dip, '\0', sizeof (dip));
1488 rc = backend_tx_begin(BACKEND_TYPE_NORMAL, &dip.di_tx);
1489 if (rc != REP_PROTOCOL_SUCCESS)
1490 return (rc);
1491
1492 rc = backend_tx_begin(BACKEND_TYPE_NONPERSIST, &dip.di_np_tx);
1493 if (rc == REP_PROTOCOL_FAIL_BACKEND_ACCESS ||
1494 rc == REP_PROTOCOL_FAIL_BACKEND_READONLY)
1495 dip.di_np_tx = NULL;
1496 else if (rc != REP_PROTOCOL_SUCCESS) {
1497 backend_tx_rollback(dip.di_tx);
1498 return (rc);
1499 }
1500
1501 if ((rc = (*info[type].obj_delete_start)(pp, &dip)) !=
1502 REP_PROTOCOL_SUCCESS) {
1503 goto fail;
1504 }
1505
1506 while (delete_stack_pop(&dip, &de)) {
1507 rc = (*de.de_cb)(&dip, &de);
1508 if (rc != REP_PROTOCOL_SUCCESS)
1509 goto fail;
1510 }
1511
1512 rc = backend_tx_commit(dip.di_tx);
1513 if (rc != REP_PROTOCOL_SUCCESS)
1514 backend_tx_rollback(dip.di_np_tx);
1515 else if (dip.di_np_tx)
1516 (void) backend_tx_commit(dip.di_np_tx);
1517
1518 delete_stack_cleanup(&dip);
1519
1520 return (rc);
1521
1522 fail:
1523 backend_tx_rollback(dip.di_tx);
1524 backend_tx_rollback(dip.di_np_tx);
1525 delete_stack_cleanup(&dip);
1526 return (rc);
1527 }
1528
1529 int
object_do_create(backend_tx_t * tx,child_info_t * cip,rc_node_t * pp,uint32_t type,const char * name,rc_node_t ** cpp)1530 object_do_create(backend_tx_t *tx, child_info_t *cip, rc_node_t *pp,
1531 uint32_t type, const char *name, rc_node_t **cpp)
1532 {
1533 uint32_t ptype = pp->rn_id.rl_type;
1534
1535 backend_query_t *q;
1536 uint32_t id;
1537 rc_node_t *np = NULL;
1538 int rc;
1539 object_info_t *ip;
1540
1541 rc_node_lookup_t *lp = &cip->ci_base_nl;
1542
1543 assert(ptype > 0 && ptype < NUM_INFO);
1544
1545 ip = &info[ptype];
1546
1547 if (type == REP_PROTOCOL_ENTITY_PROPERTYGRP)
1548 return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
1549
1550 if (ip->obj_setup_child_info == NULL ||
1551 ip->obj_query_child == NULL ||
1552 ip->obj_insert_child == NULL)
1553 return (REP_PROTOCOL_FAIL_BAD_REQUEST);
1554
1555 if ((rc = (*ip->obj_setup_child_info)(pp, type, cip)) !=
1556 REP_PROTOCOL_SUCCESS)
1557 return (rc);
1558
1559 q = backend_query_alloc();
1560 if ((rc = (*ip->obj_query_child)(q, lp, name)) !=
1561 REP_PROTOCOL_SUCCESS) {
1562 assert(rc == REP_PROTOCOL_FAIL_BAD_REQUEST);
1563 backend_query_free(q);
1564 return (rc);
1565 }
1566
1567 rc = backend_tx_run_single_int(tx, q, &id);
1568 backend_query_free(q);
1569
1570 if (rc == REP_PROTOCOL_SUCCESS)
1571 return (REP_PROTOCOL_FAIL_EXISTS);
1572 else if (rc != REP_PROTOCOL_FAIL_NOT_FOUND)
1573 return (rc);
1574
1575 if ((lp->rl_main_id = backend_new_id(tx,
1576 info[type].obj_id_space)) == 0) {
1577 return (REP_PROTOCOL_FAIL_NO_RESOURCES);
1578 }
1579
1580 if ((np = rc_node_alloc()) == NULL)
1581 return (REP_PROTOCOL_FAIL_NO_RESOURCES);
1582
1583 if ((rc = (*ip->obj_insert_child)(tx, lp, name)) !=
1584 REP_PROTOCOL_SUCCESS) {
1585 rc_node_destroy(np);
1586 return (rc);
1587 }
1588
1589 *cpp = np;
1590 return (REP_PROTOCOL_SUCCESS);
1591 }
1592
1593 /*
1594 * Fails with
1595 * _NOT_APPLICABLE - type is _PROPERTYGRP
1596 * _BAD_REQUEST - cannot create children for this type of node
1597 * name is invalid
1598 * _TYPE_MISMATCH - object cannot have children of type type
1599 * _NO_RESOURCES - out of memory, or could not allocate new id
1600 * _BACKEND_READONLY
1601 * _BACKEND_ACCESS
1602 * _EXISTS - child already exists
1603 */
1604 int
object_create(rc_node_t * pp,uint32_t type,const char * name,rc_node_t ** cpp)1605 object_create(rc_node_t *pp, uint32_t type, const char *name, rc_node_t **cpp)
1606 {
1607 backend_tx_t *tx;
1608 rc_node_t *np = NULL;
1609 child_info_t ci;
1610 int rc;
1611
1612 if ((rc = backend_tx_begin(pp->rn_id.rl_backend, &tx)) !=
1613 REP_PROTOCOL_SUCCESS) {
1614 return (rc);
1615 }
1616
1617 if ((rc = object_do_create(tx, &ci, pp, type, name, &np)) !=
1618 REP_PROTOCOL_SUCCESS) {
1619 backend_tx_rollback(tx);
1620 return (rc);
1621 }
1622
1623 rc = backend_tx_commit(tx);
1624 if (rc != REP_PROTOCOL_SUCCESS) {
1625 rc_node_destroy(np);
1626 return (rc);
1627 }
1628
1629 *cpp = rc_node_setup(np, &ci.ci_base_nl, name, ci.ci_parent);
1630
1631 return (REP_PROTOCOL_SUCCESS);
1632 }
1633
1634 /*ARGSUSED*/
1635 int
object_create_pg(rc_node_t * pp,uint32_t type,const char * name,const char * pgtype,uint32_t flags,rc_node_t ** cpp)1636 object_create_pg(rc_node_t *pp, uint32_t type, const char *name,
1637 const char *pgtype, uint32_t flags, rc_node_t **cpp)
1638 {
1639 uint32_t ptype = pp->rn_id.rl_type;
1640 backend_tx_t *tx_ro, *tx_wr;
1641 backend_query_t *q;
1642 uint32_t id;
1643 uint32_t gen = 0;
1644 rc_node_t *np = NULL;
1645 int rc;
1646 int rc_wr;
1647 int rc_ro;
1648 object_info_t *ip;
1649
1650 int nonpersist = (flags & SCF_PG_FLAG_NONPERSISTENT);
1651
1652 child_info_t ci;
1653 rc_node_lookup_t *lp = &ci.ci_base_nl;
1654
1655 assert(ptype > 0 && ptype < NUM_INFO);
1656
1657 if (ptype != REP_PROTOCOL_ENTITY_SERVICE &&
1658 ptype != REP_PROTOCOL_ENTITY_INSTANCE)
1659 return (REP_PROTOCOL_FAIL_BAD_REQUEST);
1660
1661 ip = &info[ptype];
1662
1663 assert(ip->obj_setup_child_info != NULL &&
1664 ip->obj_query_child != NULL &&
1665 ip->obj_insert_pg_child != NULL);
1666
1667 if ((rc = (*ip->obj_setup_child_info)(pp, type, &ci)) !=
1668 REP_PROTOCOL_SUCCESS)
1669 return (rc);
1670
1671 q = backend_query_alloc();
1672 if ((rc = (*ip->obj_query_child)(q, lp, name)) !=
1673 REP_PROTOCOL_SUCCESS) {
1674 backend_query_free(q);
1675 return (rc);
1676 }
1677
1678 if (!nonpersist) {
1679 lp->rl_backend = BACKEND_TYPE_NORMAL;
1680 rc_wr = backend_tx_begin(BACKEND_TYPE_NORMAL, &tx_wr);
1681 rc_ro = backend_tx_begin_ro(BACKEND_TYPE_NONPERSIST, &tx_ro);
1682 } else {
1683 lp->rl_backend = BACKEND_TYPE_NONPERSIST;
1684 rc_ro = backend_tx_begin_ro(BACKEND_TYPE_NORMAL, &tx_ro);
1685 rc_wr = backend_tx_begin(BACKEND_TYPE_NONPERSIST, &tx_wr);
1686 }
1687
1688 if (rc_wr != REP_PROTOCOL_SUCCESS) {
1689 rc = rc_wr;
1690 goto fail;
1691 }
1692 if (rc_ro != REP_PROTOCOL_SUCCESS &&
1693 rc_ro != REP_PROTOCOL_FAIL_BACKEND_ACCESS) {
1694 rc = rc_ro;
1695 goto fail;
1696 }
1697
1698 if (tx_ro != NULL) {
1699 rc = backend_tx_run_single_int(tx_ro, q, &id);
1700
1701 if (rc == REP_PROTOCOL_SUCCESS) {
1702 backend_query_free(q);
1703 rc = REP_PROTOCOL_FAIL_EXISTS;
1704 goto fail;
1705 } else if (rc != REP_PROTOCOL_FAIL_NOT_FOUND) {
1706 backend_query_free(q);
1707 goto fail;
1708 }
1709 }
1710
1711 rc = backend_tx_run_single_int(tx_wr, q, &id);
1712 backend_query_free(q);
1713
1714 if (rc == REP_PROTOCOL_SUCCESS) {
1715 rc = REP_PROTOCOL_FAIL_EXISTS;
1716 goto fail;
1717 } else if (rc != REP_PROTOCOL_FAIL_NOT_FOUND) {
1718 goto fail;
1719 }
1720
1721 if (tx_ro != NULL)
1722 backend_tx_end_ro(tx_ro);
1723 tx_ro = NULL;
1724
1725 if ((lp->rl_main_id = backend_new_id(tx_wr,
1726 info[type].obj_id_space)) == 0) {
1727 rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
1728 goto fail;
1729 }
1730
1731 if ((np = rc_node_alloc()) == NULL) {
1732 rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
1733 goto fail;
1734 }
1735
1736 if ((rc = (*ip->obj_insert_pg_child)(tx_wr, lp, name, pgtype, flags,
1737 gen)) != REP_PROTOCOL_SUCCESS) {
1738 rc_node_destroy(np);
1739 goto fail;
1740 }
1741
1742 rc = backend_tx_commit(tx_wr);
1743 if (rc != REP_PROTOCOL_SUCCESS) {
1744 rc_node_destroy(np);
1745 return (rc);
1746 }
1747
1748 *cpp = rc_node_setup_pg(np, lp, name, pgtype, flags, gen, ci.ci_parent);
1749
1750 return (REP_PROTOCOL_SUCCESS);
1751
1752 fail:
1753 if (tx_ro != NULL)
1754 backend_tx_end_ro(tx_ro);
1755 if (tx_wr != NULL)
1756 backend_tx_rollback(tx_wr);
1757 return (rc);
1758 }
1759
1760 /*
1761 * Given a row of snaplevel number, snaplevel id, service id, service name,
1762 * instance id, & instance name, create a rc_snaplevel_t & prepend it onto the
1763 * rs_levels list of the rc_snapshot_t passed in as data.
1764 * Returns _CONTINUE on success or _ABORT if any allocations fail.
1765 */
1766 /*ARGSUSED*/
1767 static int
fill_snapshot_cb(void * data,int columns,char ** vals,char ** names)1768 fill_snapshot_cb(void *data, int columns, char **vals, char **names)
1769 {
1770 rc_snapshot_t *sp = data;
1771 rc_snaplevel_t *lvl;
1772 char *num = vals[0];
1773 char *id = vals[1];
1774 char *service_id = vals[2];
1775 char *service = vals[3];
1776 char *instance_id = vals[4];
1777 char *instance = vals[5];
1778 assert(columns == 6);
1779
1780 lvl = uu_zalloc(sizeof (*lvl));
1781 if (lvl == NULL)
1782 return (BACKEND_CALLBACK_ABORT);
1783 lvl->rsl_parent = sp;
1784 lvl->rsl_next = sp->rs_levels;
1785 sp->rs_levels = lvl;
1786
1787 string_to_id(num, &lvl->rsl_level_num, "snap_level_num");
1788 string_to_id(id, &lvl->rsl_level_id, "snap_level_id");
1789 string_to_id(service_id, &lvl->rsl_service_id, "snap_level_service_id");
1790 if (instance_id != NULL)
1791 string_to_id(instance_id, &lvl->rsl_instance_id,
1792 "snap_level_instance_id");
1793
1794 lvl->rsl_scope = (const char *)"localhost";
1795 lvl->rsl_service = strdup(service);
1796 if (lvl->rsl_service == NULL) {
1797 uu_free(lvl);
1798 return (BACKEND_CALLBACK_ABORT);
1799 }
1800 if (instance) {
1801 assert(lvl->rsl_instance_id != 0);
1802 lvl->rsl_instance = strdup(instance);
1803 if (lvl->rsl_instance == NULL) {
1804 free((void *)lvl->rsl_instance);
1805 uu_free(lvl);
1806 return (BACKEND_CALLBACK_ABORT);
1807 }
1808 } else {
1809 assert(lvl->rsl_instance_id == 0);
1810 }
1811
1812 return (BACKEND_CALLBACK_CONTINUE);
1813 }
1814
1815 /*
1816 * Populate sp's rs_levels list from the snaplevel_tbl table.
1817 * Fails with
1818 * _NO_RESOURCES
1819 */
1820 int
object_fill_snapshot(rc_snapshot_t * sp)1821 object_fill_snapshot(rc_snapshot_t *sp)
1822 {
1823 backend_query_t *q;
1824 rc_snaplevel_t *sl;
1825 int result;
1826 int i;
1827
1828 q = backend_query_alloc();
1829 backend_query_add(q,
1830 "SELECT snap_level_num, snap_level_id, "
1831 " snap_level_service_id, snap_level_service, "
1832 " snap_level_instance_id, snap_level_instance "
1833 "FROM snaplevel_tbl "
1834 "WHERE snap_id = %d "
1835 "ORDER BY snap_level_id DESC",
1836 sp->rs_snap_id);
1837
1838 result = backend_run(BACKEND_TYPE_NORMAL, q, fill_snapshot_cb, sp);
1839 if (result == REP_PROTOCOL_DONE)
1840 result = REP_PROTOCOL_FAIL_NO_RESOURCES;
1841 backend_query_free(q);
1842
1843 if (result == REP_PROTOCOL_SUCCESS) {
1844 i = 0;
1845 for (sl = sp->rs_levels; sl != NULL; sl = sl->rsl_next) {
1846 if (sl->rsl_level_num != ++i) {
1847 backend_panic("snaplevels corrupt; expected "
1848 "level %d, got %d", i, sl->rsl_level_num);
1849 }
1850 }
1851 }
1852 return (result);
1853 }
1854
1855 /*
1856 * This represents a property group in a snapshot.
1857 */
1858 typedef struct check_snapshot_elem {
1859 uint32_t cse_parent;
1860 uint32_t cse_pg_id;
1861 uint32_t cse_pg_gen;
1862 char cse_seen;
1863 } check_snapshot_elem_t;
1864
1865 #define CSI_MAX_PARENTS COMPOSITION_DEPTH
1866 typedef struct check_snapshot_info {
1867 size_t csi_count;
1868 size_t csi_array_size;
1869 check_snapshot_elem_t *csi_array;
1870 size_t csi_nparents;
1871 uint32_t csi_parent_ids[CSI_MAX_PARENTS];
1872 } check_snapshot_info_t;
1873
1874 /*ARGSUSED*/
1875 static int
check_snapshot_fill_cb(void * data,int columns,char ** vals,char ** names)1876 check_snapshot_fill_cb(void *data, int columns, char **vals, char **names)
1877 {
1878 check_snapshot_info_t *csip = data;
1879 check_snapshot_elem_t *cur;
1880 const char *parent;
1881 const char *pg_id;
1882 const char *pg_gen_id;
1883
1884 if (columns == 1) {
1885 uint32_t *target;
1886
1887 if (csip->csi_nparents >= CSI_MAX_PARENTS)
1888 backend_panic("snaplevel table has too many elements");
1889
1890 target = &csip->csi_parent_ids[csip->csi_nparents++];
1891 string_to_id(vals[0], target, "snap_level_*_id");
1892
1893 return (BACKEND_CALLBACK_CONTINUE);
1894 }
1895
1896 assert(columns == 3);
1897
1898 parent = vals[0];
1899 pg_id = vals[1];
1900 pg_gen_id = vals[2];
1901
1902 if (csip->csi_count == csip->csi_array_size) {
1903 size_t newsz = (csip->csi_array_size > 0) ?
1904 csip->csi_array_size * 2 : 8;
1905 check_snapshot_elem_t *new = uu_zalloc(newsz * sizeof (*new));
1906
1907 if (new == NULL)
1908 return (BACKEND_CALLBACK_ABORT);
1909
1910 (void) memcpy(new, csip->csi_array,
1911 sizeof (*new) * csip->csi_array_size);
1912 uu_free(csip->csi_array);
1913 csip->csi_array = new;
1914 csip->csi_array_size = newsz;
1915 }
1916
1917 cur = &csip->csi_array[csip->csi_count++];
1918
1919 string_to_id(parent, &cur->cse_parent, "snap_level_*_id");
1920 string_to_id(pg_id, &cur->cse_pg_id, "snaplvl_pg_id");
1921 string_to_id(pg_gen_id, &cur->cse_pg_gen, "snaplvl_gen_id");
1922 cur->cse_seen = 0;
1923
1924 return (BACKEND_CALLBACK_CONTINUE);
1925 }
1926
1927 static int
check_snapshot_elem_cmp(const void * lhs_arg,const void * rhs_arg)1928 check_snapshot_elem_cmp(const void *lhs_arg, const void *rhs_arg)
1929 {
1930 const check_snapshot_elem_t *lhs = lhs_arg;
1931 const check_snapshot_elem_t *rhs = rhs_arg;
1932
1933 if (lhs->cse_parent < rhs->cse_parent)
1934 return (-1);
1935 if (lhs->cse_parent > rhs->cse_parent)
1936 return (1);
1937
1938 if (lhs->cse_pg_id < rhs->cse_pg_id)
1939 return (-1);
1940 if (lhs->cse_pg_id > rhs->cse_pg_id)
1941 return (1);
1942
1943 if (lhs->cse_pg_gen < rhs->cse_pg_gen)
1944 return (-1);
1945 if (lhs->cse_pg_gen > rhs->cse_pg_gen)
1946 return (1);
1947
1948 return (0);
1949 }
1950
1951 /*ARGSUSED*/
1952 static int
check_snapshot_check_cb(void * data,int columns,char ** vals,char ** names)1953 check_snapshot_check_cb(void *data, int columns, char **vals, char **names)
1954 {
1955 check_snapshot_info_t *csip = data;
1956 check_snapshot_elem_t elem;
1957 check_snapshot_elem_t *cur;
1958
1959 const char *parent = vals[0];
1960 const char *pg_id = vals[1];
1961 const char *pg_gen_id = vals[2];
1962
1963 assert(columns == 3);
1964
1965 string_to_id(parent, &elem.cse_parent, "snap_level_*_id");
1966 string_to_id(pg_id, &elem.cse_pg_id, "snaplvl_pg_id");
1967 string_to_id(pg_gen_id, &elem.cse_pg_gen, "snaplvl_gen_id");
1968
1969 if ((cur = bsearch(&elem, csip->csi_array, csip->csi_count,
1970 sizeof (*csip->csi_array), check_snapshot_elem_cmp)) == NULL)
1971 return (BACKEND_CALLBACK_ABORT);
1972
1973 if (cur->cse_seen)
1974 backend_panic("duplicate property group reported");
1975 cur->cse_seen = 1;
1976 return (BACKEND_CALLBACK_CONTINUE);
1977 }
1978
1979 /*
1980 * Check that a snapshot matches up with the latest in the repository.
1981 * Returns:
1982 * REP_PROTOCOL_SUCCESS if it is up-to-date,
1983 * REP_PROTOCOL_DONE if it is out-of-date, or
1984 * REP_PROTOCOL_FAIL_NO_RESOURCES if we ran out of memory.
1985 */
1986 static int
object_check_snapshot(uint32_t snap_id)1987 object_check_snapshot(uint32_t snap_id)
1988 {
1989 check_snapshot_info_t csi;
1990 backend_query_t *q;
1991 int result;
1992 size_t idx;
1993
1994 /* if the snapshot has never been taken, it must be out of date. */
1995 if (snap_id == 0)
1996 return (REP_PROTOCOL_DONE);
1997
1998 (void) memset(&csi, '\0', sizeof (csi));
1999
2000 q = backend_query_alloc();
2001 backend_query_add(q,
2002 "SELECT\n"
2003 " CASE snap_level_instance_id\n"
2004 " WHEN 0 THEN snap_level_service_id\n"
2005 " ELSE snap_level_instance_id\n"
2006 " END\n"
2007 "FROM snaplevel_tbl\n"
2008 "WHERE snap_id = %d;\n"
2009 "\n"
2010 "SELECT\n"
2011 " CASE snap_level_instance_id\n"
2012 " WHEN 0 THEN snap_level_service_id\n"
2013 " ELSE snap_level_instance_id\n"
2014 " END,\n"
2015 " snaplvl_pg_id,\n"
2016 " snaplvl_gen_id\n"
2017 "FROM snaplevel_tbl, snaplevel_lnk_tbl\n"
2018 "WHERE\n"
2019 " (snaplvl_level_id = snap_level_id AND\n"
2020 " snap_id = %d);",
2021 snap_id, snap_id);
2022
2023 result = backend_run(BACKEND_TYPE_NORMAL, q, check_snapshot_fill_cb,
2024 &csi);
2025 if (result == REP_PROTOCOL_DONE)
2026 result = REP_PROTOCOL_FAIL_NO_RESOURCES;
2027 backend_query_free(q);
2028
2029 if (result != REP_PROTOCOL_SUCCESS)
2030 goto fail;
2031
2032 if (csi.csi_count > 0) {
2033 qsort(csi.csi_array, csi.csi_count, sizeof (*csi.csi_array),
2034 check_snapshot_elem_cmp);
2035 }
2036
2037 #if COMPOSITION_DEPTH == 2
2038 if (csi.csi_nparents != COMPOSITION_DEPTH) {
2039 result = REP_PROTOCOL_DONE;
2040 goto fail;
2041 }
2042
2043 q = backend_query_alloc();
2044 backend_query_add(q,
2045 "SELECT "
2046 " pg_parent_id, pg_id, pg_gen_id "
2047 "FROM "
2048 " pg_tbl "
2049 "WHERE (pg_parent_id = %d OR pg_parent_id = %d)",
2050 csi.csi_parent_ids[0], csi.csi_parent_ids[1]);
2051
2052 result = backend_run(BACKEND_TYPE_NORMAL, q, check_snapshot_check_cb,
2053 &csi);
2054 #else
2055 #error This code must be updated
2056 #endif
2057 /*
2058 * To succeed, the callback must not have aborted, and we must have
2059 * found all of the items.
2060 */
2061 if (result == REP_PROTOCOL_SUCCESS) {
2062 for (idx = 0; idx < csi.csi_count; idx++) {
2063 if (csi.csi_array[idx].cse_seen == 0) {
2064 result = REP_PROTOCOL_DONE;
2065 goto fail;
2066 }
2067 }
2068 }
2069
2070 fail:
2071 uu_free(csi.csi_array);
2072 return (result);
2073 }
2074
2075 /*ARGSUSED*/
2076 static int
object_copy_string(void * data_arg,int columns,char ** vals,char ** names)2077 object_copy_string(void *data_arg, int columns, char **vals, char **names)
2078 {
2079 char **data = data_arg;
2080
2081 assert(columns == 1);
2082
2083 if (*data != NULL)
2084 free(*data);
2085 *data = NULL;
2086
2087 if (vals[0] != NULL) {
2088 if ((*data = strdup(vals[0])) == NULL)
2089 return (BACKEND_CALLBACK_ABORT);
2090 }
2091
2092 return (BACKEND_CALLBACK_CONTINUE);
2093 }
2094
2095 struct snaplevel_add_info {
2096 backend_query_t *sai_q;
2097 uint32_t sai_level_id;
2098 int sai_used; /* sai_q has been used */
2099 };
2100
2101 /*ARGSUSED*/
2102 static int
object_snaplevel_process_pg(void * data_arg,int columns,char ** vals,char ** names)2103 object_snaplevel_process_pg(void *data_arg, int columns, char **vals,
2104 char **names)
2105 {
2106 struct snaplevel_add_info *data = data_arg;
2107
2108 assert(columns == 5);
2109
2110 backend_query_add(data->sai_q,
2111 "INSERT INTO snaplevel_lnk_tbl "
2112 " (snaplvl_level_id, snaplvl_pg_id, snaplvl_pg_name, "
2113 " snaplvl_pg_type, snaplvl_pg_flags, snaplvl_gen_id)"
2114 "VALUES (%d, %s, '%q', '%q', %s, %s);",
2115 data->sai_level_id, vals[0], vals[1], vals[2], vals[3], vals[4]);
2116
2117 data->sai_used = 1;
2118
2119 return (BACKEND_CALLBACK_CONTINUE);
2120 }
2121
2122 /*ARGSUSED*/
2123 static int
object_snapshot_add_level(backend_tx_t * tx,uint32_t snap_id,uint32_t snap_level_num,uint32_t svc_id,const char * svc_name,uint32_t inst_id,const char * inst_name)2124 object_snapshot_add_level(backend_tx_t *tx, uint32_t snap_id,
2125 uint32_t snap_level_num, uint32_t svc_id, const char *svc_name,
2126 uint32_t inst_id, const char *inst_name)
2127 {
2128 struct snaplevel_add_info data;
2129 backend_query_t *q;
2130 int result;
2131
2132 assert((snap_level_num == 1 && inst_name != NULL) ||
2133 snap_level_num == 2 && inst_name == NULL);
2134
2135 data.sai_level_id = backend_new_id(tx, BACKEND_ID_SNAPLEVEL);
2136 if (data.sai_level_id == 0) {
2137 return (REP_PROTOCOL_FAIL_NO_RESOURCES);
2138 }
2139
2140 result = backend_tx_run_update(tx,
2141 "INSERT INTO snaplevel_tbl "
2142 " (snap_id, snap_level_num, snap_level_id, "
2143 " snap_level_service_id, snap_level_service, "
2144 " snap_level_instance_id, snap_level_instance) "
2145 "VALUES (%d, %d, %d, %d, %Q, %d, %Q);",
2146 snap_id, snap_level_num, data.sai_level_id, svc_id, svc_name,
2147 inst_id, inst_name);
2148
2149 q = backend_query_alloc();
2150 backend_query_add(q,
2151 "SELECT pg_id, pg_name, pg_type, pg_flags, pg_gen_id FROM pg_tbl "
2152 "WHERE (pg_parent_id = %d);",
2153 (inst_name != NULL)? inst_id : svc_id);
2154
2155 data.sai_q = backend_query_alloc();
2156 data.sai_used = 0;
2157 result = backend_tx_run(tx, q, object_snaplevel_process_pg,
2158 &data);
2159 backend_query_free(q);
2160
2161 if (result == REP_PROTOCOL_SUCCESS && data.sai_used != 0)
2162 result = backend_tx_run(tx, data.sai_q, NULL, NULL);
2163 backend_query_free(data.sai_q);
2164
2165 return (result);
2166 }
2167
2168 /*
2169 * Fails with:
2170 * _NO_RESOURCES - no new id or out of disk space
2171 * _BACKEND_READONLY - persistent backend is read-only
2172 */
2173 static int
object_snapshot_do_take(uint32_t instid,const char * inst_name,uint32_t svcid,const char * svc_name,backend_tx_t ** tx_out,uint32_t * snapid_out)2174 object_snapshot_do_take(uint32_t instid, const char *inst_name,
2175 uint32_t svcid, const char *svc_name,
2176 backend_tx_t **tx_out, uint32_t *snapid_out)
2177 {
2178 backend_tx_t *tx;
2179 backend_query_t *q;
2180 int result;
2181
2182 char *svc_name_alloc = NULL;
2183 char *inst_name_alloc = NULL;
2184 uint32_t snapid;
2185
2186 result = backend_tx_begin(BACKEND_TYPE_NORMAL, &tx);
2187 if (result != REP_PROTOCOL_SUCCESS)
2188 return (result);
2189
2190 snapid = backend_new_id(tx, BACKEND_ID_SNAPSHOT);
2191 if (snapid == 0) {
2192 result = REP_PROTOCOL_FAIL_NO_RESOURCES;
2193 goto fail;
2194 }
2195
2196 if (svc_name == NULL) {
2197 q = backend_query_alloc();
2198 backend_query_add(q,
2199 "SELECT svc_name FROM service_tbl "
2200 "WHERE (svc_id = %d)", svcid);
2201 result = backend_tx_run(tx, q, object_copy_string,
2202 &svc_name_alloc);
2203 backend_query_free(q);
2204
2205 svc_name = svc_name_alloc;
2206
2207 if (result == REP_PROTOCOL_DONE) {
2208 result = REP_PROTOCOL_FAIL_NO_RESOURCES;
2209 goto fail;
2210 }
2211 if (result == REP_PROTOCOL_SUCCESS && svc_name == NULL)
2212 backend_panic("unable to find name for svc id %d\n",
2213 svcid);
2214
2215 if (result != REP_PROTOCOL_SUCCESS)
2216 goto fail;
2217 }
2218
2219 if (inst_name == NULL) {
2220 q = backend_query_alloc();
2221 backend_query_add(q,
2222 "SELECT instance_name FROM instance_tbl "
2223 "WHERE (instance_id = %d)", instid);
2224 result = backend_tx_run(tx, q, object_copy_string,
2225 &inst_name_alloc);
2226 backend_query_free(q);
2227
2228 inst_name = inst_name_alloc;
2229
2230 if (result == REP_PROTOCOL_DONE) {
2231 result = REP_PROTOCOL_FAIL_NO_RESOURCES;
2232 goto fail;
2233 }
2234
2235 if (result == REP_PROTOCOL_SUCCESS && inst_name == NULL)
2236 backend_panic(
2237 "unable to find name for instance id %d\n", instid);
2238
2239 if (result != REP_PROTOCOL_SUCCESS)
2240 goto fail;
2241 }
2242
2243 result = object_snapshot_add_level(tx, snapid, 1,
2244 svcid, svc_name, instid, inst_name);
2245
2246 if (result != REP_PROTOCOL_SUCCESS)
2247 goto fail;
2248
2249 result = object_snapshot_add_level(tx, snapid, 2,
2250 svcid, svc_name, 0, NULL);
2251
2252 if (result != REP_PROTOCOL_SUCCESS)
2253 goto fail;
2254
2255 *snapid_out = snapid;
2256 *tx_out = tx;
2257
2258 free(svc_name_alloc);
2259 free(inst_name_alloc);
2260
2261 return (REP_PROTOCOL_SUCCESS);
2262
2263 fail:
2264 backend_tx_rollback(tx);
2265 free(svc_name_alloc);
2266 free(inst_name_alloc);
2267 return (result);
2268 }
2269
2270 /*
2271 * Fails with:
2272 * _TYPE_MISMATCH - pp is not an instance
2273 * _NO_RESOURCES - no new id or out of disk space
2274 * _BACKEND_READONLY - persistent backend is read-only
2275 */
2276 int
object_snapshot_take_new(rc_node_t * pp,const char * svc_name,const char * inst_name,const char * name,rc_node_t ** outp)2277 object_snapshot_take_new(rc_node_t *pp,
2278 const char *svc_name, const char *inst_name,
2279 const char *name, rc_node_t **outp)
2280 {
2281 rc_node_lookup_t *insti = &pp->rn_id;
2282
2283 uint32_t instid = insti->rl_main_id;
2284 uint32_t svcid = insti->rl_ids[ID_SERVICE];
2285 uint32_t snapid = 0;
2286 backend_tx_t *tx = NULL;
2287 child_info_t ci;
2288 rc_node_t *np;
2289 int result;
2290
2291 if (insti->rl_type != REP_PROTOCOL_ENTITY_INSTANCE)
2292 return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
2293
2294 result = object_snapshot_do_take(instid, inst_name, svcid, svc_name,
2295 &tx, &snapid);
2296 if (result != REP_PROTOCOL_SUCCESS)
2297 return (result);
2298
2299 if ((result = object_do_create(tx, &ci, pp,
2300 REP_PROTOCOL_ENTITY_SNAPSHOT, name, &np)) != REP_PROTOCOL_SUCCESS) {
2301 backend_tx_rollback(tx);
2302 return (result);
2303 }
2304
2305 /*
2306 * link the new object to the new snapshot.
2307 */
2308 np->rn_snapshot_id = snapid;
2309
2310 result = backend_tx_run_update(tx,
2311 "UPDATE snapshot_lnk_tbl SET lnk_snap_id = %d WHERE lnk_id = %d;",
2312 snapid, ci.ci_base_nl.rl_main_id);
2313 if (result != REP_PROTOCOL_SUCCESS) {
2314 backend_tx_rollback(tx);
2315 rc_node_destroy(np);
2316 return (result);
2317 }
2318 result = backend_tx_commit(tx);
2319 if (result != REP_PROTOCOL_SUCCESS) {
2320 rc_node_destroy(np);
2321 return (result);
2322 }
2323
2324 *outp = rc_node_setup(np, &ci.ci_base_nl, name, ci.ci_parent);
2325 return (REP_PROTOCOL_SUCCESS);
2326 }
2327
2328 /*
2329 * Fails with:
2330 * _TYPE_MISMATCH - pp is not an instance
2331 * _NO_RESOURCES - no new id or out of disk space
2332 * _BACKEND_READONLY - persistent backend is read-only
2333 */
2334 int
object_snapshot_attach(rc_node_lookup_t * snapi,uint32_t * snapid_ptr,int takesnap)2335 object_snapshot_attach(rc_node_lookup_t *snapi, uint32_t *snapid_ptr,
2336 int takesnap)
2337 {
2338 uint32_t svcid = snapi->rl_ids[ID_SERVICE];
2339 uint32_t instid = snapi->rl_ids[ID_INSTANCE];
2340 uint32_t snapid = *snapid_ptr;
2341 uint32_t oldsnapid = 0;
2342 backend_tx_t *tx = NULL;
2343 backend_query_t *q;
2344 int result;
2345
2346 delete_info_t dip;
2347 delete_ent_t de;
2348
2349 if (snapi->rl_type != REP_PROTOCOL_ENTITY_SNAPSHOT)
2350 return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
2351
2352 if (takesnap) {
2353 /* first, check that we're actually out of date */
2354 if (object_check_snapshot(snapid) == REP_PROTOCOL_SUCCESS)
2355 return (REP_PROTOCOL_SUCCESS);
2356
2357 result = object_snapshot_do_take(instid, NULL,
2358 svcid, NULL, &tx, &snapid);
2359 if (result != REP_PROTOCOL_SUCCESS)
2360 return (result);
2361 } else {
2362 result = backend_tx_begin(BACKEND_TYPE_NORMAL, &tx);
2363 if (result != REP_PROTOCOL_SUCCESS)
2364 return (result);
2365 }
2366
2367 q = backend_query_alloc();
2368 backend_query_add(q,
2369 "SELECT lnk_snap_id FROM snapshot_lnk_tbl WHERE lnk_id = %d; "
2370 "UPDATE snapshot_lnk_tbl SET lnk_snap_id = %d WHERE lnk_id = %d;",
2371 snapi->rl_main_id, snapid, snapi->rl_main_id);
2372 result = backend_tx_run_single_int(tx, q, &oldsnapid);
2373 backend_query_free(q);
2374
2375 if (result == REP_PROTOCOL_FAIL_NOT_FOUND) {
2376 backend_tx_rollback(tx);
2377 backend_panic("unable to find snapshot id %d",
2378 snapi->rl_main_id);
2379 }
2380 if (result != REP_PROTOCOL_SUCCESS)
2381 goto fail;
2382
2383 /*
2384 * Now we use the delete stack to handle the possible unreferencing
2385 * of oldsnapid.
2386 */
2387 (void) memset(&dip, 0, sizeof (dip));
2388 dip.di_tx = tx;
2389 dip.di_np_tx = NULL; /* no need for non-persistant backend */
2390
2391 if ((result = delete_stack_push(&dip, BACKEND_TYPE_NORMAL,
2392 &snaplevel_tbl_delete, oldsnapid, 0)) != REP_PROTOCOL_SUCCESS)
2393 goto fail;
2394
2395 while (delete_stack_pop(&dip, &de)) {
2396 result = (*de.de_cb)(&dip, &de);
2397 if (result != REP_PROTOCOL_SUCCESS)
2398 goto fail;
2399 }
2400
2401 result = backend_tx_commit(tx);
2402 if (result != REP_PROTOCOL_SUCCESS)
2403 goto fail;
2404
2405 delete_stack_cleanup(&dip);
2406 *snapid_ptr = snapid;
2407 return (REP_PROTOCOL_SUCCESS);
2408
2409 fail:
2410 backend_tx_rollback(tx);
2411 delete_stack_cleanup(&dip);
2412 return (result);
2413 }
2414