From 2fb668b005557d1851cbcef2b386e8d3694d960a Mon Sep 17 00:00:00 2001 From: zacheiss Date: Thu, 26 Apr 2001 21:26:06 +0000 Subject: [PATCH] Add queries for creating, deleting, and modifying containers, as well as for adding and removing machines from them. --- server/mr_server.h | 13 ++ server/qaccess.pc | 63 +++++++++ server/qfollow.pc | 26 ++++ server/qrtn.pc | 6 + server/qsetup.pc | 40 ++++++ server/qsupport.pc | 229 ++++++++++++++++++++++++++++++++ server/queries2.c | 315 ++++++++++++++++++++++++++++++++++++++++++++- 7 files changed, 691 insertions(+), 1 deletion(-) diff --git a/server/mr_server.h b/server/mr_server.h index 804e6943..33921e71 100644 --- a/server/mr_server.h +++ b/server/mr_server.h @@ -152,6 +152,7 @@ int access_ahal(struct query *q, char *argv[], client *cl); int access_snt(struct query *q, char *argv[], client *cl); int access_printer(struct query *q, char *argv[], client *cl); int access_zephyr(struct query *q, char *argv[], client *cl); +int access_container(struct query *q, char *argv[], client *cl); /* prototypes from qfollow.pc */ int followup_fix_modby(struct query *q, struct save_queue *sq, @@ -187,6 +188,9 @@ int followup_ghst(struct query *q, struct save_queue *sq, struct validate *v, int followup_gpsv(struct query *q, struct save_queue *sq, struct validate *v, int (*action)(int, char **, void *), void *actarg, client *cl); +int followup_gcon(struct query *q, struct save_queue *sq, struct validate *v, + int (*action)(int, char **, void *), void *actarg, + client *cl); int followup_ausr(struct query *q, char *argv[], client *cl); int followup_aqot(struct query *q, char *argv[], client *cl); @@ -231,6 +235,7 @@ int setup_ahal(struct query *q, char *argv[], client *cl); int setup_uhha(struct query *q, char *argv[], client *cl); int setup_aprn(struct query *q, char *argv[], client *cl); int setup_dpsv(struct query *q, char *argv[], client *cl); +int setup_dcon(struct query *q, char *argv[], client *cl); /* prototypes from qsupport.pc */ int set_pobox(struct query *q, char *argv[], client *cl); @@ -240,6 +245,7 @@ int delete_member_from_list(struct query *q, char *argv[], client *cl); int tag_member_of_list(struct query *q, char *argv[], client *cl); int register_user(struct query *q, char *argv[], client *cl); int do_user_reservation(struct query *q, char *argv[], client *cl); +int update_container(struct query *q, char *argv[], client *cl); int get_ace_use(struct query *q, char **argv, client *cl, int (*action)(int, char *[], void *), void *actarg); @@ -263,6 +269,13 @@ int get_user_reservations(struct query *q, char **argv, client *cl, int get_user_by_reservation(struct query *q, char **argv, client *cl, int (*action)(int, char *[], void *), void *actarg); +int get_machines_of_container(struct query *q, char **argv, client *cl, + int (*action)(int, char *[], void *), + void *actarg); +int get_subcontainers_of_container(struct query *q, char **argv, client *cl, + int (*action)(int, char *[], void *), + void *actarg); + /* prototypes from qvalidate.pc */ int validate_fields(struct query *q, char *argv[], struct valobj *vo, int n); diff --git a/server/qaccess.pc b/server/qaccess.pc index 5820f283..2f6a4934 100644 --- a/server/qaccess.pc +++ b/server/qaccess.pc @@ -619,3 +619,66 @@ int access_zephyr(struct query *q, char *argv[], client *cl) return MR_PERM; } +/* access_container - check access for most container operations + * + * Inputs: argv[0] - cnt_id + * q - query name + * cl - client name + * + * - check if that client is a member of the access control list + * - OR, if the query is add_machine_to_container or delete_machine_from_container + * check if the client is a memeber of the mem_acl list + * - if the query is update_container and the container is to be renamed and + * it is a top-level container, only priviledged users can do it + */ + +int access_container(struct query *q, char *argv[], client *cl) +{ + EXEC SQL BEGIN DECLARE SECTION; + int cnt_id, acl_id, memacl_id; + char acl_type[CONTAINERS_ACL_TYPE_SIZE], memacl_type[CONTAINERS_ACL_TYPE_SIZE]; + char name[CONTAINERS_NAME_SIZE], *newname; + EXEC SQL END DECLARE SECTION; + int status; + + cnt_id = *(int *)argv[0]; + + /* if amcn or dmcn, container id is the second argument */ + if (strcmp(q->shortname, "amcn") == 0 || strcmp(q->shortname, "dmcn") == 0) + cnt_id = *(int *)argv[1]; + + EXEC SQL SELECT acl_id, acl_type, memacl_id, memacl_type, name + INTO :acl_id, :acl_type, :memacl_id, :memacl_type, :name + FROM containers + WHERE cnt_id = :cnt_id; + + if (sqlca.sqlerrd[2] != 1) + return MR_INTERNAL; + + /* trim off the trailing spaces */ + strcpy(name, strtrim(name)); + + /* if the query is update_container and the containers is to be renamed + * and it is a top-level container, only dbadmin can do it */ + if (!strcmp(q->shortname, "ucon")) + { + newname = argv[1]; + if (strcmp(name, newname) && strchr(name, '/') == NULL) + return MR_PERM; + } + + /* check for client in access control list and return success right + * away if it's there. */ + if (find_member(acl_type, acl_id, cl)) + return MR_SUCCESS; + + /* If not amcn, dmcn, we lose. */ + if (strcmp(q->shortname, "amcn") && strcmp(q->shortname, "dmcn")) + return MR_PERM; + + if (find_member(memacl_type, memacl_id, cl)) + return MR_SUCCESS; + + /* Otherwise fail. */ + return MR_PERM; +} diff --git a/server/qfollow.pc b/server/qfollow.pc index ac6cb510..09a9d1af 100644 --- a/server/qfollow.pc +++ b/server/qfollow.pc @@ -967,3 +967,29 @@ int trigger_dcm(struct query *q, char *argv[], client *cl) return MR_SUCCESS; } } + +/* followup_gcon: fix the ace_name, memace_name, and modby */ + +int followup_gcon(struct query *q, struct save_queue *sq, struct validate *v, + int (*action)(int, char *[], void *), void *actarg, + client *cl) +{ + char **argv; + int status; + + while (sq_get_data(sq, &argv)) + { + mr_trim_args(q->vcnt, argv); + + status = fix_ace(argv[4], &argv[5]); + if (status && status != MR_NO_MATCH) + return status; + + status = fix_ace(argv[6], &argv[7]); + if (status && status != MR_NO_MATCH) + return status; + } + + return followup_fix_modby(q, sq, v, action, actarg, cl); +} + diff --git a/server/qrtn.pc b/server/qrtn.pc index cfcd0226..42892e2a 100644 --- a/server/qrtn.pc +++ b/server/qrtn.pc @@ -1010,6 +1010,9 @@ int name_to_id(char *name, enum tables type, int *id) } EXEC SQL SELECT string_id INTO :j FROM strings WHERE string = :iname; break; + case CONTAINERS_TABLE: + EXEC SQL SELECT cnt_id INTO :j FROM containers WHERE name = :iname; + break; default: return MR_INTERNAL; } @@ -1064,6 +1067,9 @@ int id_to_name(int id, enum tables type, char **name) case STRINGS_TABLE: EXEC SQL SELECT string INTO :iname FROM strings WHERE string_id = :j; break; + case CONTAINERS_TABLE: + EXEC SQL SELECT name INTO :iname FROM containers WHERE cnt_id = :j; + break; default: return MR_INTERNAL; } diff --git a/server/qsetup.pc b/server/qsetup.pc index 11a4da78..ab50d669 100644 --- a/server/qsetup.pc +++ b/server/qsetup.pc @@ -220,6 +220,10 @@ int setup_dmac(struct query *q, char *argv[], client *cl) return MR_IN_USE; EXEC SQL DELETE FROM mcmap WHERE mach_id = :id; + if (dbms_errno) + return mr_errcode; + + EXEC SQL DELETE FROM mcntmap WHERE mach_id = :id; if (dbms_errno) return mr_errcode; return MR_SUCCESS; @@ -1257,6 +1261,42 @@ int setup_dpsv(struct query *q, char **argv, client *cl) return MR_SUCCESS; } +int setup_dcon(struct query *q, char *argv[], client *cl) +{ + EXEC SQL BEGIN DECLARE SECTION; + int id, cnt; + char containername[CONTAINERS_NAME_SIZE]; + EXEC SQL END DECLARE SECTION; + + id = *(int *)argv[0]; + /* check to see if there are machines in this container */ + EXEC SQL SELECT COUNT(mach_id) INTO :cnt FROM mcntmap + WHERE cnt_id = :id; + if (cnt > 0) + return MR_IN_USE; + + /* check to see if there are subcontainers in this container */ + + /* get the container name */ + + EXEC SQL SELECT name INTO :containername + FROM containers + WHERE cnt_id = :id; + + /* trim off the trailing spaces */ + strcpy(containername, strtrim(containername)); + + EXEC SQL SELECT COUNT(cnt_id) INTO :cnt FROM containers + WHERE name LIKE :containername || '/' || '%'; + + if (cnt > 0) + return MR_IN_USE; + + if (dbms_errno) + return mr_errcode; + return MR_SUCCESS; +} + /* hostname_check() * validate the rfc1035/rfc1123-ness of a hostname */ diff --git a/server/qsupport.pc b/server/qsupport.pc index 40250f32..c6bbe15f 100644 --- a/server/qsupport.pc +++ b/server/qsupport.pc @@ -1851,3 +1851,232 @@ int get_user_by_reservation(struct query *q, char **argv, client *cl, return MR_SUCCESS; } + +int update_container(struct query *q, char *argv[], client *cl) +{ + EXEC SQL BEGIN DECLARE SECTION; + int cnt_id, acl_id, memacl_id, who; + char name[CONTAINERS_NAME_SIZE], newchildname[CONTAINERS_NAME_SIZE]; + char* newname, *entity, *description, *location, *contact, *acl_type, *memacl_type; + EXEC SQL END DECLARE SECTION; + char* tmpchar; + int cnt, childid; + char childname[CONTAINERS_NAME_SIZE]; + + cnt_id = *(int *)argv[0]; + newname = argv[1]; + description = argv[2]; + location = argv[3]; + contact = argv[4]; + acl_type = argv[5]; + acl_id = *(int *)argv[6]; + memacl_type = argv[7]; + memacl_id = *(int *)argv[8]; + entity = cl->entity; + who = cl->client_id; + + EXEC SQL SELECT name INTO :name + FROM containers + WHERE cnt_id = :cnt_id; + + /* trim off the trailing spaces */ + strcpy(name, strtrim(name)); + + /* if we are renaming the container */ + if (strcmp(name, newname)) + { + /* make sure that only the name part of the name has been changed */ + tmpchar = strrchr(name, '/'); + /* not a top-level name */ + if (tmpchar) + { + cnt = tmpchar - name + 1; + /* the parent part of the old and new name should be identical */ + if (strncmp(name, newname, cnt)) + return MR_NEW_CONTAINER_NAME; + } + /* top level name, new name should be a top level name too */ + else + { + if (strrchr(newname, '/')) + return MR_NEW_CONTAINER_NAME; + } + + /* update the name for this container */ + EXEC SQL UPDATE containers + SET name = :newname + WHERE cnt_id = :cnt_id; + + if (dbms_errno) + return mr_errcode; + + /* get names for its child containers */ + EXEC SQL DECLARE csr_ucon CURSOR FOR + SELECT name, cnt_id FROM containers WHERE name LIKE :name || '/' || '%'; + + EXEC SQL OPEN csr_ucon; + if (dbms_errno) + return mr_errcode; + + while (1) + { + EXEC SQL FETCH csr_ucon INTO :childname, :childid; + if (sqlca.sqlcode) + break; + + /* concatenate the new parent name with the existing sub-container name + * we get the sub-containers new name */ + tmpchar = childname + strlen(name); + strcpy(newchildname, newname); + strcat(newchildname, tmpchar); + + /* update the name */ + EXEC SQL UPDATE containers + SET name = :newchildname, modtime = SYSDATE, modby = :who, modwith = :entity + WHERE cnt_id = :childid; + + if (sqlca.sqlcode) + break; + } + + EXEC SQL CLOSE csr_ucon; + if (dbms_errno) + return mr_errcode; + } + + /* update the remaining fields */ + EXEC SQL UPDATE containers + SET description = NVL(:description, CHR(0)), location = NVL(:location, CHR(0)), + contact = NVL(:contact, CHR(0)), acl_type = :acl_type, acl_id = :acl_id, + memacl_type = :memacl_type, memacl_id = :memacl_id, + modtime = SYSDATE, modby = :who, modwith = :entity + WHERE cnt_id = :cnt_id; + + if (dbms_errno) + return mr_errcode; + + return MR_SUCCESS; +} + +int get_machines_of_container(struct query *q, char *argv[], client *cl, + int (*action)(int, char *[], void *), void *actarg) +{ + EXEC SQL BEGIN DECLARE SECTION; + int cnt_id, isrecursive; + char machinename[MACHINE_NAME_SIZE], containername[CONTAINERS_NAME_SIZE]; + char *qs; + EXEC SQL END DECLARE SECTION; + + char querystring[512], tmp [256]; + char *rargv[2]; + int found = 0; + + rargv[0] = machinename; + rargv[1] = containername; + + cnt_id = *(int *)argv[0]; + isrecursive = *(int *)argv[1]; + + /* get the container name */ + + EXEC SQL SELECT name INTO :containername + FROM containers + WHERE cnt_id = :cnt_id; + + /* trim off the trailing spaces */ + strcpy(containername, strtrim(containername)); + + strcpy(querystring, "SELECT a.name, b.name FROM machine a, containers b, mcntmap c "); + strcat(querystring, "WHERE a.mach_id = c.mach_id AND b.cnt_id = c.cnt_id "); + + if (!isrecursive) + sprintf(tmp, "AND b.cnt_id = %d ", cnt_id); + else + sprintf(tmp, "AND (b.cnt_id = %d OR b.name LIKE '%s/%%') ", cnt_id, containername); + + strcat(querystring, tmp); + strcat(querystring, "ORDER BY b.name, a.name"); + + qs = querystring; + + EXEC SQL PREPARE stmt FROM :qs; + if (sqlca.sqlcode) + return MR_INTERNAL; + EXEC SQL DECLARE curs_gmnm CURSOR FOR stmt; + EXEC SQL OPEN curs_gmnm; + + while (1) + { + EXEC SQL FETCH curs_gmnm INTO :machinename, :containername; + if (sqlca.sqlcode) + break; + (*action)(2, rargv, actarg); + found++; + } + + EXEC SQL CLOSE curs_gmnm; + if (!found) + return MR_NO_MATCH; + return MR_SUCCESS; +} + +int get_subcontainers_of_container(struct query *q, char *argv[], client *cl, + int (*action)(int, char *[], void *), void *actarg) +{ + EXEC SQL BEGIN DECLARE SECTION; + int cnt_id, isrecursive; + char containername[CONTAINERS_NAME_SIZE], subcontainername[CONTAINERS_NAME_SIZE]; + char *qs; + EXEC SQL END DECLARE SECTION; + + char querystring[512], tmp [256]; + char *rargv[1]; + int found = 0; + + rargv[0] = subcontainername; + + cnt_id = *(int *)argv[0]; + isrecursive = *(int *)argv[1]; + + /* get the container name */ + + EXEC SQL SELECT name INTO :containername + FROM containers + WHERE cnt_id = :cnt_id; + + /* trim off the trailing spaces */ + strcpy(containername, strtrim(containername)); + + strcpy(querystring, "SELECT name FROM containers "); + + if (!isrecursive) + sprintf(tmp, "WHERE name LIKE '%s/%%' and name NOT LIKE '%s/%%/%%' ", containername, + containername); + else + sprintf(tmp, "WHERE name LIKE '%s/%%' ", containername); + + strcat(querystring, tmp); + strcat(querystring, "ORDER BY name"); + + qs = querystring; + + EXEC SQL PREPARE stmt FROM :qs; + if (sqlca.sqlcode) + return MR_INTERNAL; + EXEC SQL DECLARE curs_gsoc CURSOR FOR stmt; + EXEC SQL OPEN curs_gsoc; + + while (1) + { + EXEC SQL FETCH curs_gsoc INTO :subcontainername; + if (sqlca.sqlcode) + break; + (*action)(1, rargv, actarg); + found++; + } + + EXEC SQL CLOSE curs_gsoc; + if (!found) + return MR_NO_MATCH; + return MR_SUCCESS; +} diff --git a/server/queries2.c b/server/queries2.c index 811f6e01..e3229a75 100644 --- a/server/queries2.c +++ b/server/queries2.c @@ -42,6 +42,10 @@ static struct valobj VOfilesys0[] = { {V_ID, 0, FILESYS_TABLE, "label", "filsys_id", MR_FILESYS}, }; +static struct valobj VOcon0[] = { + {V_ID, 0, CONTAINERS_TABLE, "name", "cnt_id", MR_CONTAINER}, +}; + static struct valobj VOnum0[] = { {V_NUM, 0}, }; @@ -3108,7 +3112,163 @@ static char *gdds_fields[] = { "sid", }; - +static char *gcon_fields[] = { + "name", + "name", "description", "location", "contact", + "ace_type", "ace_name", "memace_type", "memace_name", "modtime", "modby", "modwith", +}; + +static struct validate gcon_validate = { + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + followup_gcon, +}; + +static char *acon_fields[] = { + "name", "description", "location", "contact", + "ace_type", "ace_name", "memace_type", "memace_name", +}; + +static struct valobj acon_valobj[] = { + {V_CHAR, 0, CONTAINERS_TABLE, "name"}, + {V_LEN, 1, CONTAINERS_TABLE, "description"}, + {V_CHAR, 2, CONTAINERS_TABLE, "location"}, + {V_CHAR, 3, CONTAINERS_TABLE, "contact"}, + {V_TYPE, 4, 0, "ace_type", 0, MR_ACE}, + {V_TYPEDATA, 5, 0, 0, 0, MR_ACE}, + {V_TYPE, 6, 0, "ace_type", 0, MR_ACE}, + {V_TYPEDATA, 7, 0, 0, 0, MR_ACE}, +}; + +static struct validate acon_validate = +{ + acon_valobj, + 8, + "name", + "name = '%s'", + 1, + "cnt_id", + 0, + prefetch_value, + set_modtime, +}; + +static char *ucon_fields[] = { + "name", + "newname", "description", "location", "contact", + "ace_type", "ace_name", "memace_type", "memace_name", +}; + +static struct valobj ucon_valobj[] = { + {V_ID, 0, CONTAINERS_TABLE, "name", "cnt_id", MR_CONTAINER}, + {V_RENAME, 1, CONTAINERS_TABLE, "name", "cnt_id", MR_NOT_UNIQUE}, + {V_LEN, 2, CONTAINERS_TABLE, "description"}, + {V_CHAR, 3, CONTAINERS_TABLE, "location"}, + {V_CHAR, 4, CONTAINERS_TABLE, "contact"}, + {V_TYPE, 5, 0, "ace_type", 0, MR_ACE}, + {V_TYPEDATA, 6, 0, 0, 0, MR_ACE}, + {V_TYPE, 7, 0, "ace_type", 0, MR_ACE}, + {V_TYPEDATA, 8, 0, 0, 0, MR_ACE}, +}; + +static struct validate ucon_validate = +{ + ucon_valobj, + 9, + 0, + 0, + 0, + 0, + access_container, + 0, + update_container, +}; + +static char *dcon_fields[] = { + "name", +}; + +static struct validate dcon_validate = +{ + VOcon0, + 1, + 0, + 0, + 0, + 0, + 0, + setup_dcon, + 0, +}; + +static char *amcn_fields[] = { + "machine", "container", +}; + +static struct valobj amcn_valobj[] = /* ADD_MACHINE_TO_CONTAINER */ +{ /* DELETE_MACHINE_FROM_CONTAINER */ + {V_ID, 0, MACHINE_TABLE, "name", "mach_id", MR_MACHINE}, + {V_ID, 1, CONTAINERS_TABLE, "name", "cnt_id", MR_CONTAINER}, +}; + +static struct validate amcn_validate = /* for amtn and dmfn */ +{ + amcn_valobj, + 2, + "mach_id", + "mach_id = %d and cnt_id = %d", + 2, + 0, + access_container, + 0, + set_mach_modtime_by_id, +}; + +static char *gmoc_fields[] = { + "container", + "isrecursive", + "machine", + "container", +}; + +static struct validate gmoc_validate = +{ + VOcon0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + get_machines_of_container, +}; + +static char *gsoc_fields[] = { + "container", + "isrecursive", + "subcontainer", +}; + +static struct validate gsoc_validate = +{ + VOcon0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + get_subcontainers_of_container, +}; + /* Generalized Query Definitions */ /* Multiple versions of the same query MUST be listed in ascending @@ -6426,6 +6586,159 @@ struct query Queries[] = { NULL, }, + { + /* Q_GCON - GET_CONTAINER, v7 */ + "get_container", + "gcon", + 7, + RETRIEVE, + "c", + CONTAINERS_TABLE, + "name, description, location, contact, acl_type, acl_id, memacl_type, memacl_id, TO_CHAR(modtime, 'DD-mon-YYYY HH24:MI:SS'), modby, modwith FROM containers", + gcon_fields, + 11, + "name = '%s' AND cnt_id != 0", + 1, + NULL, + &gcon_validate, + }, + + { + /* Q_ACON - ADD_CONTAINER, v7 */ /* uses prefetch_value() for cnt_id */ + "add_container", + "acon", + 7, + APPEND, + "c", + CONTAINERS_TABLE, + "INTO containers (name, description, location, contact, acl_type, acl_id, memacl_type, memacl_id, cnt_id) VALUES ('%s', NVL('%s', CHR(0)), NVL('%s', CHR(0)), NVL('%s', CHR(0)), '%s', %d, '%s', %d, %s)", + acon_fields, + 8, + 0, + 0, + NULL, + &acon_validate, + }, + + { + /* Q_UCON - UPDATE_CONTAINER, v7 */ + "update_container", + "ucon", + 7, + UPDATE, + 0, + CONTAINERS_TABLE, + 0, + ucon_fields, + 8, + 0, + 1, + NULL, + &ucon_validate, + }, + + { + /* Q_DCON - DELETE_CONTAINER, v7 */ + "delete_container", + "dcon", + 7, + DELETE, + "c", + CONTAINERS_TABLE, + NULL, + dcon_fields, + 0, + "cnt_id = %d", + 1, + NULL, + &dcon_validate, + }, + + { + /* Q_AMCN - ADD_MACHINE_TO_CONTAINER, v7 */ + "add_machine_to_container", + "amcn", + 7, + APPEND, + "mcn", + MCNTMAP_TABLE, + "INTO mcntmap (mach_id, cnt_id) VALUES (%d, %d)", + amcn_fields, + 2, + 0, + 0, + NULL, + &amcn_validate, + }, + + { + /* Q_DMCN - DELETE_MACHINE_FROM_CONTAINER, v7 */ + "delete_machine_from_container", + "dmcn", + 7, + DELETE, + "mcn", + MCNTMAP_TABLE, + 0, + amcn_fields, + 0, + "mach_id = %d AND cnt_id = %d", + 2, + NULL, + &amcn_validate, + }, + + { + /* Q_GMNM - GET_MACHINE_TO_CONTAINER_MAP, v7 */ + "get_machine_to_container_map", + "gmnm", + 7, + RETRIEVE, + "mcn", + MCNTMAP_TABLE, + "c.name FROM machine m, containers c, mcntmap mcn", + amcn_fields, + 1, + "m.name = UPPER('%s') AND mcn.cnt_id = c.cnt_id AND mcn.mach_id = m.mach_id", + 1, + NULL, + NULL, + }, + + { + /* Q_GMOC - GET_MACHINES_OF_CONTAINER, v7 */ + "get_machines_of_container", + "gmoc", + 7, + RETRIEVE, + NULL, + MCNTMAP_TABLE, + NULL, + gmoc_fields, + 2, + NULL, + 2, + NULL, + &gmoc_validate, + }, + + { + /* Q_GSOC - GET_SUBCONTAINERS_OF_CONTAINER, v7 */ + "get_subcontainers_of_container", + "gsoc", + 7, + RETRIEVE, + NULL, + CONTAINERS_TABLE, + NULL, + gsoc_fields, + 1, + NULL, + 2, + NULL, + &gsoc_validate, + }, + }; int QueryCount = (sizeof(Queries) / sizeof(struct query)); -- 2.45.2