1f06ca4afSHartmut Brandt /* 2f06ca4afSHartmut Brandt * Copyright (c) 2001-2003 3f06ca4afSHartmut Brandt * Fraunhofer Institute for Open Communication Systems (FhG Fokus). 4f06ca4afSHartmut Brandt * All rights reserved. 5f06ca4afSHartmut Brandt * 6f06ca4afSHartmut Brandt * Author: Harti Brandt <harti@freebsd.org> 7f06ca4afSHartmut Brandt * 8896052c1SHartmut Brandt * Redistribution and use in source and binary forms, with or without 9896052c1SHartmut Brandt * modification, are permitted provided that the following conditions 10896052c1SHartmut Brandt * are met: 11896052c1SHartmut Brandt * 1. Redistributions of source code must retain the above copyright 12896052c1SHartmut Brandt * notice, this list of conditions and the following disclaimer. 13f06ca4afSHartmut Brandt * 2. Redistributions in binary form must reproduce the above copyright 14f06ca4afSHartmut Brandt * notice, this list of conditions and the following disclaimer in the 15f06ca4afSHartmut Brandt * documentation and/or other materials provided with the distribution. 16f06ca4afSHartmut Brandt * 17896052c1SHartmut Brandt * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18896052c1SHartmut Brandt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19896052c1SHartmut Brandt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20896052c1SHartmut Brandt * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 21896052c1SHartmut Brandt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22896052c1SHartmut Brandt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23896052c1SHartmut Brandt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24896052c1SHartmut Brandt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25896052c1SHartmut Brandt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26896052c1SHartmut Brandt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27896052c1SHartmut Brandt * SUCH DAMAGE. 28f06ca4afSHartmut Brandt * 29896052c1SHartmut Brandt * $Begemot: bsnmp/snmpd/export.c,v 1.7 2004/08/06 08:47:11 brandt Exp $ 30f06ca4afSHartmut Brandt * 31f06ca4afSHartmut Brandt * Support functions for modules. 32f06ca4afSHartmut Brandt */ 33f06ca4afSHartmut Brandt #include <sys/types.h> 34f06ca4afSHartmut Brandt #include <sys/un.h> 35f06ca4afSHartmut Brandt #include <stdio.h> 36f06ca4afSHartmut Brandt #include <stdlib.h> 37f06ca4afSHartmut Brandt #include <string.h> 38f06ca4afSHartmut Brandt #include <syslog.h> 39f06ca4afSHartmut Brandt #include <stdarg.h> 40f06ca4afSHartmut Brandt 41f06ca4afSHartmut Brandt #include "snmpmod.h" 42f06ca4afSHartmut Brandt #include "snmpd.h" 43f06ca4afSHartmut Brandt #include "tree.h" 44f06ca4afSHartmut Brandt 45f06ca4afSHartmut Brandt /* 46f06ca4afSHartmut Brandt * Support functions 47f06ca4afSHartmut Brandt */ 48f06ca4afSHartmut Brandt 49f06ca4afSHartmut Brandt /* 50f06ca4afSHartmut Brandt * This is user for SET of string variables. If 'req' is not -1 then 51f06ca4afSHartmut Brandt * the arguments is checked to be of that length. The old value is saved 52f06ca4afSHartmut Brandt * in scratch->ptr1 and the new value is allocated and copied. 53f06ca4afSHartmut Brandt * If there is an old values it must have been allocated by malloc. 54f06ca4afSHartmut Brandt */ 55f06ca4afSHartmut Brandt int 56f06ca4afSHartmut Brandt string_save(struct snmp_value *value, struct snmp_context *ctx, 57f06ca4afSHartmut Brandt ssize_t req_size, u_char **valp) 58f06ca4afSHartmut Brandt { 59f06ca4afSHartmut Brandt if (req_size != -1 && value->v.octetstring.len != (u_long)req_size) 60f06ca4afSHartmut Brandt return (SNMP_ERR_BADVALUE); 61f06ca4afSHartmut Brandt 62f06ca4afSHartmut Brandt ctx->scratch->ptr1 = *valp; 63f06ca4afSHartmut Brandt 64f06ca4afSHartmut Brandt if ((*valp = malloc(value->v.octetstring.len + 1)) == NULL) { 65f06ca4afSHartmut Brandt *valp = ctx->scratch->ptr1; 66f06ca4afSHartmut Brandt return (SNMP_ERR_RES_UNAVAIL); 67f06ca4afSHartmut Brandt } 68f06ca4afSHartmut Brandt 69f06ca4afSHartmut Brandt memcpy(*valp, value->v.octetstring.octets, value->v.octetstring.len); 70f06ca4afSHartmut Brandt (*valp)[value->v.octetstring.len] = '\0'; 71f06ca4afSHartmut Brandt 72f06ca4afSHartmut Brandt return (0); 73f06ca4afSHartmut Brandt } 74f06ca4afSHartmut Brandt 75f06ca4afSHartmut Brandt /* 76f06ca4afSHartmut Brandt * Commit a string. This is easy - free the old value. 77f06ca4afSHartmut Brandt */ 78f06ca4afSHartmut Brandt void 79f06ca4afSHartmut Brandt string_commit(struct snmp_context *ctx) 80f06ca4afSHartmut Brandt { 81f06ca4afSHartmut Brandt free(ctx->scratch->ptr1); 82f06ca4afSHartmut Brandt } 83f06ca4afSHartmut Brandt 84f06ca4afSHartmut Brandt /* 85f06ca4afSHartmut Brandt * Rollback a string - free new value and copy back old one. 86f06ca4afSHartmut Brandt */ 87f06ca4afSHartmut Brandt void 88f06ca4afSHartmut Brandt string_rollback(struct snmp_context *ctx, u_char **valp) 89f06ca4afSHartmut Brandt { 90f06ca4afSHartmut Brandt free(*valp); 91f06ca4afSHartmut Brandt *valp = ctx->scratch->ptr1; 92f06ca4afSHartmut Brandt } 93f06ca4afSHartmut Brandt 94f06ca4afSHartmut Brandt /* 95f06ca4afSHartmut Brandt * ROLLBACK or COMMIT fails because instance has disappeared. Free string. 96f06ca4afSHartmut Brandt */ 97f06ca4afSHartmut Brandt void 98f06ca4afSHartmut Brandt string_free(struct snmp_context *ctx) 99f06ca4afSHartmut Brandt { 100f06ca4afSHartmut Brandt free(ctx->scratch->ptr1); 101f06ca4afSHartmut Brandt } 102f06ca4afSHartmut Brandt 103f06ca4afSHartmut Brandt /* 104f06ca4afSHartmut Brandt * Get a string value for a response packet 105f06ca4afSHartmut Brandt */ 106f06ca4afSHartmut Brandt int 107f06ca4afSHartmut Brandt string_get(struct snmp_value *value, const u_char *ptr, ssize_t len) 108f06ca4afSHartmut Brandt { 109f06ca4afSHartmut Brandt if (ptr == NULL) { 110f06ca4afSHartmut Brandt value->v.octetstring.len = 0; 111f06ca4afSHartmut Brandt value->v.octetstring.octets = NULL; 112f06ca4afSHartmut Brandt return (SNMP_ERR_NOERROR); 113f06ca4afSHartmut Brandt } 114f06ca4afSHartmut Brandt if (len == -1) 115f06ca4afSHartmut Brandt len = strlen(ptr); 116f06ca4afSHartmut Brandt value->v.octetstring.len = (u_long)len; 117f06ca4afSHartmut Brandt if ((value->v.octetstring.octets = malloc((size_t)len)) == NULL) 118f06ca4afSHartmut Brandt return (SNMP_ERR_RES_UNAVAIL); 119f06ca4afSHartmut Brandt memcpy(value->v.octetstring.octets, ptr, (size_t)len); 120f06ca4afSHartmut Brandt return (SNMP_ERR_NOERROR); 121f06ca4afSHartmut Brandt } 122f06ca4afSHartmut Brandt 123f06ca4afSHartmut Brandt /* 124f06ca4afSHartmut Brandt * Support for IPADDRESS 125f06ca4afSHartmut Brandt * 126f06ca4afSHartmut Brandt * Save the old IP address in scratch->int1 and set the new one. 127f06ca4afSHartmut Brandt */ 128f06ca4afSHartmut Brandt int 129f06ca4afSHartmut Brandt ip_save(struct snmp_value *value, struct snmp_context *ctx, u_char *valp) 130f06ca4afSHartmut Brandt { 131f06ca4afSHartmut Brandt ctx->scratch->int1 = (valp[0] << 24) | (valp[1] << 16) | (valp[2] << 8) 132f06ca4afSHartmut Brandt | valp[3]; 133f06ca4afSHartmut Brandt 134f06ca4afSHartmut Brandt valp[0] = value->v.ipaddress[0]; 135f06ca4afSHartmut Brandt valp[1] = value->v.ipaddress[1]; 136f06ca4afSHartmut Brandt valp[2] = value->v.ipaddress[2]; 137f06ca4afSHartmut Brandt valp[3] = value->v.ipaddress[3]; 138f06ca4afSHartmut Brandt 139f06ca4afSHartmut Brandt return (0); 140f06ca4afSHartmut Brandt } 141f06ca4afSHartmut Brandt 142f06ca4afSHartmut Brandt /* 143f06ca4afSHartmut Brandt * Rollback the address by copying back the old one 144f06ca4afSHartmut Brandt */ 145f06ca4afSHartmut Brandt void 146f06ca4afSHartmut Brandt ip_rollback(struct snmp_context *ctx, u_char *valp) 147f06ca4afSHartmut Brandt { 148f06ca4afSHartmut Brandt valp[0] = ctx->scratch->int1 >> 24; 149f06ca4afSHartmut Brandt valp[1] = ctx->scratch->int1 >> 16; 150f06ca4afSHartmut Brandt valp[2] = ctx->scratch->int1 >> 8; 151f06ca4afSHartmut Brandt valp[3] = ctx->scratch->int1; 152f06ca4afSHartmut Brandt } 153f06ca4afSHartmut Brandt 154f06ca4afSHartmut Brandt /* 155f06ca4afSHartmut Brandt * Nothing to do for commit 156f06ca4afSHartmut Brandt */ 157f06ca4afSHartmut Brandt void 158f06ca4afSHartmut Brandt ip_commit(struct snmp_context *ctx __unused) 159f06ca4afSHartmut Brandt { 160f06ca4afSHartmut Brandt } 161f06ca4afSHartmut Brandt 162f06ca4afSHartmut Brandt /* 163f06ca4afSHartmut Brandt * Retrieve an IP address 164f06ca4afSHartmut Brandt */ 165f06ca4afSHartmut Brandt int 166f06ca4afSHartmut Brandt ip_get(struct snmp_value *value, u_char *valp) 167f06ca4afSHartmut Brandt { 168f06ca4afSHartmut Brandt value->v.ipaddress[0] = valp[0]; 169f06ca4afSHartmut Brandt value->v.ipaddress[1] = valp[1]; 170f06ca4afSHartmut Brandt value->v.ipaddress[2] = valp[2]; 171f06ca4afSHartmut Brandt value->v.ipaddress[3] = valp[3]; 172f06ca4afSHartmut Brandt return (SNMP_ERR_NOERROR); 173f06ca4afSHartmut Brandt } 174f06ca4afSHartmut Brandt 175f06ca4afSHartmut Brandt /* 176f06ca4afSHartmut Brandt * Object ID support 177f06ca4afSHartmut Brandt * 178f06ca4afSHartmut Brandt * Save the old value in a fresh allocated oid pointed to by scratch->ptr1. 179f06ca4afSHartmut Brandt */ 180f06ca4afSHartmut Brandt int 181f06ca4afSHartmut Brandt oid_save(struct snmp_value *value, struct snmp_context *ctx, 182f06ca4afSHartmut Brandt struct asn_oid *oid) 183f06ca4afSHartmut Brandt { 184f06ca4afSHartmut Brandt if ((ctx->scratch->ptr1 = malloc(sizeof(struct asn_oid))) == NULL) 185f06ca4afSHartmut Brandt return (SNMP_ERR_RES_UNAVAIL); 186f06ca4afSHartmut Brandt *(struct asn_oid *)ctx->scratch->ptr1 = *oid; 187f06ca4afSHartmut Brandt *oid = value->v.oid; 188f06ca4afSHartmut Brandt 189f06ca4afSHartmut Brandt return (0); 190f06ca4afSHartmut Brandt } 191f06ca4afSHartmut Brandt 192f06ca4afSHartmut Brandt void 193f06ca4afSHartmut Brandt oid_rollback(struct snmp_context *ctx, struct asn_oid *oid) 194f06ca4afSHartmut Brandt { 195f06ca4afSHartmut Brandt *oid = *(struct asn_oid *)ctx->scratch->ptr1; 196f06ca4afSHartmut Brandt free(ctx->scratch->ptr1); 197f06ca4afSHartmut Brandt } 198f06ca4afSHartmut Brandt 199f06ca4afSHartmut Brandt void 200f06ca4afSHartmut Brandt oid_commit(struct snmp_context *ctx) 201f06ca4afSHartmut Brandt { 202f06ca4afSHartmut Brandt free(ctx->scratch->ptr1); 203f06ca4afSHartmut Brandt } 204f06ca4afSHartmut Brandt 205f06ca4afSHartmut Brandt int 206f06ca4afSHartmut Brandt oid_get(struct snmp_value *value, const struct asn_oid *oid) 207f06ca4afSHartmut Brandt { 208f06ca4afSHartmut Brandt value->v.oid = *oid; 209f06ca4afSHartmut Brandt return (SNMP_ERR_NOERROR); 210f06ca4afSHartmut Brandt } 211f06ca4afSHartmut Brandt 212f06ca4afSHartmut Brandt /* 213f06ca4afSHartmut Brandt * Decode an index 214f06ca4afSHartmut Brandt */ 215f06ca4afSHartmut Brandt int 216f06ca4afSHartmut Brandt index_decode(const struct asn_oid *oid, u_int sub, u_int code, ...) 217f06ca4afSHartmut Brandt { 218f06ca4afSHartmut Brandt va_list ap; 219f06ca4afSHartmut Brandt u_int index_count; 220f06ca4afSHartmut Brandt void *octs[10]; 221f06ca4afSHartmut Brandt u_int nocts; 222f06ca4afSHartmut Brandt u_int idx; 223f06ca4afSHartmut Brandt 224f06ca4afSHartmut Brandt va_start(ap, code); 225f06ca4afSHartmut Brandt index_count = SNMP_INDEX_COUNT(code); 226f06ca4afSHartmut Brandt nocts = 0; 227f06ca4afSHartmut Brandt 228f06ca4afSHartmut Brandt for (idx = 0; idx < index_count; idx++) { 229f06ca4afSHartmut Brandt switch (SNMP_INDEX(code, idx)) { 230f06ca4afSHartmut Brandt 231f06ca4afSHartmut Brandt case SNMP_SYNTAX_NULL: 232f06ca4afSHartmut Brandt break; 233f06ca4afSHartmut Brandt 234f06ca4afSHartmut Brandt case SNMP_SYNTAX_INTEGER: 235f06ca4afSHartmut Brandt if (sub == oid->len) 236f06ca4afSHartmut Brandt goto err; 237f06ca4afSHartmut Brandt *va_arg(ap, int32_t *) = oid->subs[sub++]; 238f06ca4afSHartmut Brandt break; 239f06ca4afSHartmut Brandt 240f06ca4afSHartmut Brandt case SNMP_SYNTAX_COUNTER64: 241f06ca4afSHartmut Brandt if (sub == oid->len) 242f06ca4afSHartmut Brandt goto err; 243f06ca4afSHartmut Brandt *va_arg(ap, u_int64_t *) = oid->subs[sub++]; 244f06ca4afSHartmut Brandt break; 245f06ca4afSHartmut Brandt 246f06ca4afSHartmut Brandt case SNMP_SYNTAX_OCTETSTRING: 247f06ca4afSHartmut Brandt { 248f06ca4afSHartmut Brandt u_char **cval; 249f06ca4afSHartmut Brandt size_t *sval; 250f06ca4afSHartmut Brandt u_int i; 251f06ca4afSHartmut Brandt 252f06ca4afSHartmut Brandt /* only variable size supported */ 253f06ca4afSHartmut Brandt if (sub == oid->len) 254f06ca4afSHartmut Brandt goto err; 255f06ca4afSHartmut Brandt cval = va_arg(ap, u_char **); 256f06ca4afSHartmut Brandt sval = va_arg(ap, size_t *); 257f06ca4afSHartmut Brandt *sval = oid->subs[sub++]; 258f06ca4afSHartmut Brandt if (sub + *sval > oid->len) 259f06ca4afSHartmut Brandt goto err; 260f06ca4afSHartmut Brandt if ((*cval = malloc(*sval)) == NULL) { 261f06ca4afSHartmut Brandt syslog(LOG_ERR, "%s: %m", __func__); 262f06ca4afSHartmut Brandt goto err; 263f06ca4afSHartmut Brandt } 264f06ca4afSHartmut Brandt octs[nocts++] = *cval; 265f06ca4afSHartmut Brandt for (i = 0; i < *sval; i++) { 266f06ca4afSHartmut Brandt if (oid->subs[sub] > 0xff) 267f06ca4afSHartmut Brandt goto err; 268f06ca4afSHartmut Brandt (*cval)[i] = oid->subs[sub++]; 269f06ca4afSHartmut Brandt } 270f06ca4afSHartmut Brandt break; 271f06ca4afSHartmut Brandt } 272f06ca4afSHartmut Brandt 273f06ca4afSHartmut Brandt case SNMP_SYNTAX_OID: 274f06ca4afSHartmut Brandt { 275f06ca4afSHartmut Brandt struct asn_oid *aval; 276f06ca4afSHartmut Brandt u_int i; 277f06ca4afSHartmut Brandt 278f06ca4afSHartmut Brandt if (sub == oid->len) 279f06ca4afSHartmut Brandt goto err; 280f06ca4afSHartmut Brandt aval = va_arg(ap, struct asn_oid *); 281f06ca4afSHartmut Brandt aval->len = oid->subs[sub++]; 282f06ca4afSHartmut Brandt if (aval->len > ASN_MAXOIDLEN) 283f06ca4afSHartmut Brandt goto err; 284f06ca4afSHartmut Brandt for (i = 0; i < aval->len; i++) 285f06ca4afSHartmut Brandt aval->subs[i] = oid->subs[sub++]; 286f06ca4afSHartmut Brandt break; 287f06ca4afSHartmut Brandt } 288f06ca4afSHartmut Brandt 289f06ca4afSHartmut Brandt case SNMP_SYNTAX_IPADDRESS: 290f06ca4afSHartmut Brandt { 291f06ca4afSHartmut Brandt u_int8_t *pval; 292f06ca4afSHartmut Brandt u_int i; 293f06ca4afSHartmut Brandt 294f06ca4afSHartmut Brandt if (sub + 4 > oid->len) 295f06ca4afSHartmut Brandt goto err; 296f06ca4afSHartmut Brandt pval = va_arg(ap, u_int8_t *); 297f06ca4afSHartmut Brandt for (i = 0; i < 4; i++) { 298f06ca4afSHartmut Brandt if (oid->subs[sub] > 0xff) 299f06ca4afSHartmut Brandt goto err; 300f06ca4afSHartmut Brandt pval[i] = oid->subs[sub++]; 301f06ca4afSHartmut Brandt } 302f06ca4afSHartmut Brandt break; 303f06ca4afSHartmut Brandt } 304f06ca4afSHartmut Brandt 305f06ca4afSHartmut Brandt case SNMP_SYNTAX_COUNTER: 306f06ca4afSHartmut Brandt case SNMP_SYNTAX_GAUGE: 307f06ca4afSHartmut Brandt case SNMP_SYNTAX_TIMETICKS: 308f06ca4afSHartmut Brandt if (sub == oid->len) 309f06ca4afSHartmut Brandt goto err; 310f06ca4afSHartmut Brandt if (oid->subs[sub] > 0xffffffff) 311f06ca4afSHartmut Brandt goto err; 312f06ca4afSHartmut Brandt *va_arg(ap, u_int32_t *) = oid->subs[sub++]; 313f06ca4afSHartmut Brandt break; 314f06ca4afSHartmut Brandt } 315f06ca4afSHartmut Brandt } 316f06ca4afSHartmut Brandt 317f06ca4afSHartmut Brandt va_end(ap); 318f06ca4afSHartmut Brandt return (0); 319f06ca4afSHartmut Brandt 320f06ca4afSHartmut Brandt err: 321f06ca4afSHartmut Brandt va_end(ap); 322f06ca4afSHartmut Brandt while(nocts > 0) 323f06ca4afSHartmut Brandt free(octs[--nocts]); 324f06ca4afSHartmut Brandt return (-1); 325f06ca4afSHartmut Brandt } 326f06ca4afSHartmut Brandt 327f06ca4afSHartmut Brandt /* 328f06ca4afSHartmut Brandt * Compare the index part of an OID and an index. 329f06ca4afSHartmut Brandt */ 330f06ca4afSHartmut Brandt int 331f06ca4afSHartmut Brandt index_compare_off(const struct asn_oid *oid, u_int sub, 332f06ca4afSHartmut Brandt const struct asn_oid *idx, u_int off) 333f06ca4afSHartmut Brandt { 334f06ca4afSHartmut Brandt u_int i; 335f06ca4afSHartmut Brandt 336f06ca4afSHartmut Brandt for (i = off; i < idx->len && i < oid->len - sub; i++) { 337f06ca4afSHartmut Brandt if (oid->subs[sub + i] < idx->subs[i]) 338f06ca4afSHartmut Brandt return (-1); 339f06ca4afSHartmut Brandt if (oid->subs[sub + i] > idx->subs[i]) 340f06ca4afSHartmut Brandt return (+1); 341f06ca4afSHartmut Brandt } 342f06ca4afSHartmut Brandt if (oid->len - sub < idx->len) 343f06ca4afSHartmut Brandt return (-1); 344f06ca4afSHartmut Brandt if (oid->len - sub > idx->len) 345f06ca4afSHartmut Brandt return (+1); 346f06ca4afSHartmut Brandt 347f06ca4afSHartmut Brandt return (0); 348f06ca4afSHartmut Brandt } 349f06ca4afSHartmut Brandt 350f06ca4afSHartmut Brandt int 351f06ca4afSHartmut Brandt index_compare(const struct asn_oid *oid, u_int sub, const struct asn_oid *idx) 352f06ca4afSHartmut Brandt { 353f06ca4afSHartmut Brandt return (index_compare_off(oid, sub, idx, 0)); 354f06ca4afSHartmut Brandt } 355f06ca4afSHartmut Brandt 356f06ca4afSHartmut Brandt /* 357f06ca4afSHartmut Brandt * Append an index to an oid 358f06ca4afSHartmut Brandt */ 359f06ca4afSHartmut Brandt void 360f06ca4afSHartmut Brandt index_append_off(struct asn_oid *var, u_int sub, const struct asn_oid *idx, 361f06ca4afSHartmut Brandt u_int off) 362f06ca4afSHartmut Brandt { 363f06ca4afSHartmut Brandt u_int i; 364f06ca4afSHartmut Brandt 365f06ca4afSHartmut Brandt var->len = sub + idx->len; 366f06ca4afSHartmut Brandt for (i = off; i < idx->len; i++) 367f06ca4afSHartmut Brandt var->subs[sub + i] = idx->subs[i]; 368f06ca4afSHartmut Brandt } 369f06ca4afSHartmut Brandt void 370f06ca4afSHartmut Brandt index_append(struct asn_oid *var, u_int sub, const struct asn_oid *idx) 371f06ca4afSHartmut Brandt { 372f06ca4afSHartmut Brandt index_append_off(var, sub, idx, 0); 373f06ca4afSHartmut Brandt } 374f06ca4afSHartmut Brandt 375