Annotation of src/usr.bin/systat/pool.c, Revision 1.15
1.15 ! dlg 1: /* $OpenBSD: pool.c,v 1.14 2017/07/29 16:03:10 florian Exp $ */
1.1 canacar 2: /*
3: * Copyright (c) 2008 Can Erkin Acar <canacar@openbsd.org>
4: *
5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
8: *
9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16: */
17:
18: #include <sys/types.h>
1.10 deraadt 19: #include <sys/signal.h>
1.1 canacar 20: #include <sys/sysctl.h>
21: #include <sys/pool.h>
22: #include <errno.h>
23: #include <stdlib.h>
1.3 chl 24: #include <string.h>
1.10 deraadt 25: #include <limits.h>
1.1 canacar 26:
27: #include "systat.h"
28:
1.12 dlg 29: #ifndef nitems
30: #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
31: #endif
32:
33: static int sysctl_rdint(const int *, unsigned int);
34: static int hw_ncpusfound(void);
35:
36: static int pool_get_npools(void);
37: static int pool_get_name(int, char *, size_t);
38: static int pool_get_cache(int, struct kinfo_pool_cache *);
39: static int pool_get_cache_cpus(int, struct kinfo_pool_cache_cpu *,
40: unsigned int);
41:
1.1 canacar 42: void print_pool(void);
43: int read_pool(void);
44: void sort_pool(void);
45: int select_pool(void);
46: void showpool(int k);
1.8 mpi 47: int pool_keyboard_callback(int);
1.1 canacar 48:
49: /* qsort callbacks */
50: int sort_name_callback(const void *s1, const void *s2);
1.2 canacar 51: int sort_req_callback(const void *s1, const void *s2);
1.4 canacar 52: int sort_psize_callback(const void *s1, const void *s2);
53: int sort_npage_callback(const void *s1, const void *s2);
1.1 canacar 54:
55: struct pool_info {
56: char name[32];
1.7 dlg 57: struct kinfo_pool pool;
1.1 canacar 58: };
59:
1.12 dlg 60: /*
61: * the kernel gives an array of ncpusfound * kinfo_pool_cache structs, but
62: * it's idea of how big that struct is may differ from here. we fetch both
63: * ncpusfound and the size it thinks kinfo_pool_cache is from sysctl, and
64: * then allocate the memory for this here.
65: */
66: struct pool_cache_info {
67: char name[32];
68: struct kinfo_pool_cache cache;
69: struct kinfo_pool_cache_cpu *cache_cpus;
70: };
1.1 canacar 71:
1.8 mpi 72: int print_all = 0;
1.1 canacar 73: int num_pools = 0;
74: struct pool_info *pools = NULL;
1.12 dlg 75: int num_pool_caches = 0;
76: struct pool_cache_info *pool_caches = NULL;
1.1 canacar 77:
1.12 dlg 78: int ncpusfound = 0;
1.1 canacar 79:
80: field_def fields_pool[] = {
1.11 sthen 81: {"NAME", 12, 32, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
1.1 canacar 82: {"SIZE", 8, 24, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
83: {"REQUESTS", 8, 24, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
84: {"FAIL", 8, 24, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
85: {"INUSE", 8, 24, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
86: {"PGREQ", 8, 24, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
87: {"PGREL", 8, 24, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
88: {"NPAGE", 8, 24, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
89: {"HIWAT", 8, 24, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
90: {"MINPG", 8, 24, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
91: {"MAXPG", 8, 24, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
92: {"IDLE", 8, 24, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0}
93: };
94:
1.6 jasper 95: #define FLD_POOL_NAME FIELD_ADDR(fields_pool,0)
96: #define FLD_POOL_SIZE FIELD_ADDR(fields_pool,1)
97: #define FLD_POOL_REQS FIELD_ADDR(fields_pool,2)
98: #define FLD_POOL_FAIL FIELD_ADDR(fields_pool,3)
99: #define FLD_POOL_INUSE FIELD_ADDR(fields_pool,4)
100: #define FLD_POOL_PGREQ FIELD_ADDR(fields_pool,5)
101: #define FLD_POOL_PGREL FIELD_ADDR(fields_pool,6)
102: #define FLD_POOL_NPAGE FIELD_ADDR(fields_pool,7)
103: #define FLD_POOL_HIWAT FIELD_ADDR(fields_pool,8)
104: #define FLD_POOL_MINPG FIELD_ADDR(fields_pool,9)
105: #define FLD_POOL_MAXPG FIELD_ADDR(fields_pool,10)
106: #define FLD_POOL_IDLE FIELD_ADDR(fields_pool,11)
1.1 canacar 107:
108: /* Define views */
109: field_def *view_pool_0[] = {
110: FLD_POOL_NAME, FLD_POOL_SIZE, FLD_POOL_REQS, FLD_POOL_FAIL,
111: FLD_POOL_INUSE, FLD_POOL_PGREQ, FLD_POOL_PGREL, FLD_POOL_NPAGE,
112: FLD_POOL_HIWAT, FLD_POOL_MINPG, FLD_POOL_MAXPG, FLD_POOL_IDLE, NULL
113: };
114:
115: order_type pool_order_list[] = {
1.2 canacar 116: {"name", "name", 'N', sort_name_callback},
117: {"requests", "requests", 'Q', sort_req_callback},
1.4 canacar 118: {"size", "size", 'Z', sort_psize_callback},
119: {"npages", "npages", 'P', sort_npage_callback},
1.1 canacar 120: {NULL, NULL, 0, NULL}
121: };
122:
123: /* Define view managers */
124: struct view_manager pool_mgr = {
125: "Pool", select_pool, read_pool, sort_pool, print_header,
1.8 mpi 126: print_pool, pool_keyboard_callback, pool_order_list, pool_order_list
1.1 canacar 127: };
128:
1.12 dlg 129: field_view pool_view = {
130: view_pool_0,
131: "pool",
132: '5',
133: &pool_mgr
134: };
135:
136: void pool_cache_print(void);
137: int pool_cache_read(void);
138: void pool_cache_sort(void);
139: void pool_cache_show(const struct pool_cache_info *);
140: int pool_cache_kbd_cb(int);
141:
142: field_def pool_cache_fields[] = {
143: {"NAME", 12, 32, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
144: {"LEN", 4, 4, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
1.13 dlg 145: {"IDLE", 4, 4, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
1.12 dlg 146: {"NGC", 4, 4, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
147: {"CPU", 4, 4, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
148: {"REQ", 8, 12, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
149: {"REL", 8, 12, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
150: {"LREQ", 8, 12, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
151: {"LREL", 8, 12, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
152: };
153:
154: #define FLD_POOL_CACHE_NAME FIELD_ADDR(pool_cache_fields, 0)
155: #define FLD_POOL_CACHE_LEN FIELD_ADDR(pool_cache_fields, 1)
1.13 dlg 156: #define FLD_POOL_CACHE_IDLE FIELD_ADDR(pool_cache_fields, 2)
1.12 dlg 157: #define FLD_POOL_CACHE_NGC FIELD_ADDR(pool_cache_fields, 3)
158: #define FLD_POOL_CACHE_CPU FIELD_ADDR(pool_cache_fields, 4)
159: #define FLD_POOL_CACHE_GET FIELD_ADDR(pool_cache_fields, 5)
160: #define FLD_POOL_CACHE_PUT FIELD_ADDR(pool_cache_fields, 6)
161: #define FLD_POOL_CACHE_LGET FIELD_ADDR(pool_cache_fields, 7)
162: #define FLD_POOL_CACHE_LPUT FIELD_ADDR(pool_cache_fields, 8)
163:
164: field_def *view_pool_cache_0[] = {
165: FLD_POOL_CACHE_NAME,
166: FLD_POOL_CACHE_LEN,
1.13 dlg 167: FLD_POOL_CACHE_IDLE,
1.12 dlg 168: FLD_POOL_CACHE_NGC,
169: FLD_POOL_CACHE_CPU,
170: FLD_POOL_CACHE_GET,
171: FLD_POOL_CACHE_PUT,
172: FLD_POOL_CACHE_LGET,
173: FLD_POOL_CACHE_LPUT,
174: NULL,
175: };
176:
177: order_type pool_cache_order_list[] = {
178: {"name", "name", 'N', sort_name_callback},
179: {"requests", "requests", 'G', sort_req_callback},
180: {"releases", "releases", 'P', sort_req_callback},
1.1 canacar 181: {NULL, NULL, 0, NULL}
182: };
183:
1.12 dlg 184: /* Define view managers */
185: struct view_manager pool_cache_mgr = {
186: "PoolCache",
187: select_pool,
188: pool_cache_read,
189: pool_cache_sort,
190: print_header,
191: pool_cache_print,
192: pool_keyboard_callback,
193: pool_cache_order_list,
194: pool_cache_order_list
195: };
196:
197: field_view pool_cache_view = {
198: view_pool_cache_0,
199: "pcaches",
200: '5',
201: &pool_cache_mgr
202: };
1.1 canacar 203:
204: int
205: sort_name_callback(const void *s1, const void *s2)
206: {
207: struct pool_info *p1, *p2;
208: p1 = (struct pool_info *)s1;
209: p2 = (struct pool_info *)s2;
210:
1.2 canacar 211: return strcmp(p1->name, p2->name) * sortdir;
212: }
213:
214: int
215: sort_req_callback(const void *s1, const void *s2)
216: {
217: struct pool_info *p1, *p2;
218: p1 = (struct pool_info *)s1;
219: p2 = (struct pool_info *)s2;
220:
221: if (p1->pool.pr_nget < p2->pool.pr_nget)
222: return sortdir;
223: if (p1->pool.pr_nget > p2->pool.pr_nget)
224: return -sortdir;
225:
226: return sort_name_callback(s1, s2);
1.4 canacar 227: }
228:
229: int
230: sort_npage_callback(const void *s1, const void *s2)
231: {
232: struct pool_info *p1, *p2;
233: p1 = (struct pool_info *)s1;
234: p2 = (struct pool_info *)s2;
235:
236: if (p1->pool.pr_npages < p2->pool.pr_npages)
237: return sortdir;
238: if (p1->pool.pr_npages > p2->pool.pr_npages)
239: return -sortdir;
240:
241: return sort_name_callback(s1, s2);
242: }
243:
244: int
245: sort_psize_callback(const void *s1, const void *s2)
246: {
247: struct pool_info *p1, *p2;
248: size_t ps1, ps2;
249:
250: p1 = (struct pool_info *)s1;
251: p2 = (struct pool_info *)s2;
252:
253: ps1 = (size_t)(p1->pool.pr_nget - p1->pool.pr_nput) *
254: (size_t)p1->pool.pr_size;
255: ps2 = (size_t)(p2->pool.pr_nget - p2->pool.pr_nput) *
256: (size_t)p2->pool.pr_size;
257:
258: if (ps1 < ps2)
259: return sortdir;
260: if (ps1 > ps2)
261: return -sortdir;
262:
263: return sort_npage_callback(s1, s2);
1.1 canacar 264: }
265:
266: void
267: sort_pool(void)
268: {
269: order_type *ordering;
270:
271: if (curr_mgr == NULL)
272: return;
273:
274: ordering = curr_mgr->order_curr;
275:
276: if (ordering == NULL)
277: return;
278: if (ordering->func == NULL)
279: return;
280: if (pools == NULL)
281: return;
282: if (num_pools <= 0)
283: return;
284:
1.2 canacar 285: mergesort(pools, num_pools, sizeof(struct pool_info), ordering->func);
1.1 canacar 286: }
287:
288: int
289: select_pool(void)
290: {
291: num_disp = num_pools;
292: return (0);
293: }
294:
295: int
296: read_pool(void)
297: {
1.12 dlg 298: int mib[] = { CTL_KERN, KERN_POOL, KERN_POOL_POOL, 0 };
299: struct pool_info *p;
300: int np, i;
1.1 canacar 301: size_t size;
302:
1.12 dlg 303: np = pool_get_npools();
304: if (np == -1) {
1.1 canacar 305: error("sysctl(npools): %s", strerror(errno));
306: return (-1);
307: }
308:
1.12 dlg 309: if (np == 0) {
310: free(pools);
311: pools = NULL;
1.1 canacar 312: num_pools = 0;
313: return (0);
314: }
315:
316: if (np > num_pools || pools == NULL) {
1.12 dlg 317: p = reallocarray(pools, np, sizeof(*pools));
1.1 canacar 318: if (p == NULL) {
319: error("realloc: %s", strerror(errno));
320: return (-1);
321: }
1.12 dlg 322: /* commit */
1.1 canacar 323: pools = p;
324: num_pools = np;
325: }
326:
1.5 canacar 327: num_disp = num_pools;
328:
1.1 canacar 329: for (i = 0; i < num_pools; i++) {
1.12 dlg 330: p = &pools[i];
331: np = i + 1;
332:
333: mib[3] = np;
1.7 dlg 334: size = sizeof(pools[i].pool);
1.12 dlg 335: if (sysctl(mib, nitems(mib), &p->pool, &size, NULL, 0) < 0) {
336: p->name[0] = '\0';
1.5 canacar 337: num_disp--;
338: continue;
1.1 canacar 339: }
340:
1.12 dlg 341: if (pool_get_name(np, p->name, sizeof(p->name)) < 0)
342: snprintf(p->name, sizeof(p->name), "#%d#", i + 1);
1.1 canacar 343: }
344:
345: return 0;
346: }
347:
348:
349: void
350: print_pool(void)
351: {
1.8 mpi 352: struct pool_info *p;
1.5 canacar 353: int i, n, count = 0;
1.1 canacar 354:
355: if (pools == NULL)
356: return;
357:
1.5 canacar 358: for (n = i = 0; i < num_pools; i++) {
1.8 mpi 359: p = &pools[i];
360: if (p->name[0] == 0)
1.5 canacar 361: continue;
1.8 mpi 362:
363: if (!print_all &&
364: (p->pool.pr_nget == 0 && p->pool.pr_npagealloc == 0))
365: continue;
366:
1.5 canacar 367: if (n++ < dispstart)
368: continue;
369: showpool(i);
1.1 canacar 370: count++;
371: if (maxprint > 0 && count >= maxprint)
372: break;
373: }
374: }
375:
376: int
377: initpool(void)
378: {
379: field_view *v;
380:
1.12 dlg 381: add_view(&pool_view);
382: read_pool();
383:
384: ncpusfound = hw_ncpusfound();
385: if (ncpusfound == -1) {
386: error("sysctl(ncpusfound): %s", strerror(errno));
387: exit(1);
388: }
1.1 canacar 389:
1.12 dlg 390: add_view(&pool_cache_view);
391: pool_cache_read();
1.1 canacar 392:
393: return(0);
394: }
395:
396: void
397: showpool(int k)
398: {
399: struct pool_info *p = pools + k;
400:
401: if (k < 0 || k >= num_pools)
402: return;
403:
404: print_fld_str(FLD_POOL_NAME, p->name);
405: print_fld_uint(FLD_POOL_SIZE, p->pool.pr_size);
406:
407: print_fld_size(FLD_POOL_REQS, p->pool.pr_nget);
408: print_fld_size(FLD_POOL_FAIL, p->pool.pr_nfail);
409: print_fld_ssize(FLD_POOL_INUSE, p->pool.pr_nget - p->pool.pr_nput);
410: print_fld_size(FLD_POOL_PGREQ, p->pool.pr_npagealloc);
411: print_fld_size(FLD_POOL_PGREL, p->pool.pr_npagefree);
412:
413: print_fld_size(FLD_POOL_NPAGE, p->pool.pr_npages);
414: print_fld_size(FLD_POOL_HIWAT, p->pool.pr_hiwat);
415: print_fld_size(FLD_POOL_MINPG, p->pool.pr_minpages);
416:
417: if (p->pool.pr_maxpages == UINT_MAX)
418: print_fld_str(FLD_POOL_MAXPG, "inf");
419: else
420: print_fld_size(FLD_POOL_MAXPG, p->pool.pr_maxpages);
421:
422: print_fld_size(FLD_POOL_IDLE, p->pool.pr_nidle);
423:
424: end_line();
1.8 mpi 425: }
426:
427: int
428: pool_keyboard_callback(int ch)
429: {
430: switch (ch) {
431: case 'A':
432: print_all ^= 1;
433: gotsig_alarm = 1;
434: default:
435: return keyboard_callback(ch);
436: };
437:
438: return (1);
1.12 dlg 439: }
440:
441: int
442: pool_cache_read(void)
443: {
444: struct pool_cache_info *pc;
445: int np, i;
446:
447: np = pool_get_npools();
448: if (np == -1) {
449: error("sysctl(npools): %s", strerror(errno));
450: return (-1);
451: }
452:
453: if (np > num_pool_caches) {
454: pc = reallocarray(pool_caches, np, sizeof(*pc));
455: if (pc == NULL) {
456: error("realloc: %s", strerror(errno));
457: return (-1);
458: }
459: /* commit to using the new memory */
460: pool_caches = pc;
461:
462: for (i = num_pool_caches; i < np; i++) {
463: pc = &pool_caches[i];
464: pc->name[0] = '\0';
465:
466: pc->cache_cpus = reallocarray(NULL, ncpusfound,
467: sizeof(*pc->cache_cpus));
468: if (pc->cache_cpus == NULL) {
469: error("malloc cache cpus: %s", strerror(errno));
470: goto unalloc;
471: }
472: }
473:
474: /* commit to using the new cache_infos */
475: num_pool_caches = np;
476: }
477:
478: for (i = 0; i < num_pool_caches; i++) {
479: pc = &pool_caches[i];
480: np = i + 1;
481:
482: if (pool_get_cache(np, &pc->cache) < 0 ||
483: pool_get_cache_cpus(np, pc->cache_cpus, ncpusfound) < 0) {
484: pc->name[0] = '\0';
485: continue;
486: }
487:
488: if (pool_get_name(np, pc->name, sizeof(pc->name)) < 0)
489: snprintf(pc->name, sizeof(pc->name), "#%d#", i + 1);
490: }
491:
492: return 0;
493:
494: unalloc:
495: while (i > num_pool_caches) {
496: pc = &pool_caches[--i];
497: free(pc->cache_cpus);
498: }
1.14 florian 499: return (-1);
1.12 dlg 500: }
501:
502: void
503: pool_cache_sort(void)
504: {
505: /* XXX */
506: order_type *ordering;
507:
508: if (curr_mgr == NULL)
509: return;
510:
511: ordering = curr_mgr->order_curr;
512:
513: if (ordering == NULL)
514: return;
515: if (ordering->func == NULL)
516: return;
517: if (pools == NULL)
518: return;
519: if (num_pools <= 0)
520: return;
521:
522: mergesort(pools, num_pools, sizeof(struct pool_info), ordering->func);
523: }
524:
525: void
526: pool_cache_print(void)
527: {
528: struct pool_cache_info *pc;
529: int i, n, count = 0;
530:
531: if (pool_caches == NULL)
532: return;
533:
534: for (n = i = 0; i < num_pool_caches; i++) {
535: pc = &pool_caches[i];
536: if (pc->name[0] == '\0')
537: continue;
538:
539: if (n++ < dispstart)
540: continue;
541:
542: pool_cache_show(pc);
543: count++;
544: if (maxprint > 0 && count >= maxprint)
545: break;
546: }
547: }
548:
549: void
550: pool_cache_show(const struct pool_cache_info *pc)
551: {
552: const struct kinfo_pool_cache *kpc;
553: const struct kinfo_pool_cache_cpu *kpcc;
554: int cpu;
555:
556: kpc = &pc->cache;
557:
558: print_fld_str(FLD_POOL_CACHE_NAME, pc->name);
559: print_fld_uint(FLD_POOL_CACHE_LEN, kpc->pr_len);
1.13 dlg 560: print_fld_uint(FLD_POOL_CACHE_IDLE, kpc->pr_nitems);
1.15 ! dlg 561: print_fld_size(FLD_POOL_CACHE_NGC, kpc->pr_ngc);
1.12 dlg 562:
563: for (cpu = 0; cpu < ncpusfound; cpu++) {
564: kpcc = &pc->cache_cpus[cpu];
565:
566: print_fld_uint(FLD_POOL_CACHE_CPU, kpcc->pr_cpu);
567:
568: print_fld_size(FLD_POOL_CACHE_GET, kpcc->pr_nget);
569: print_fld_size(FLD_POOL_CACHE_PUT, kpcc->pr_nput);
570: print_fld_size(FLD_POOL_CACHE_LGET, kpcc->pr_nlget);
571: print_fld_size(FLD_POOL_CACHE_LPUT, kpcc->pr_nlput);
572: end_line();
573: }
574:
575: }
576:
577: static int
578: pool_get_npools(void)
579: {
580: int mib[] = { CTL_KERN, KERN_POOL, KERN_POOL_NPOOLS };
581:
582: return (sysctl_rdint(mib, nitems(mib)));
583: }
584:
585: static int
586: pool_get_cache(int pool, struct kinfo_pool_cache *kpc)
587: {
588: int mib[] = { CTL_KERN, KERN_POOL, KERN_POOL_CACHE, pool };
589: size_t len = sizeof(*kpc);
590:
591: return (sysctl(mib, nitems(mib), kpc, &len, NULL, 0));
592: }
593:
594: static int
595: pool_get_cache_cpus(int pool, struct kinfo_pool_cache_cpu *kpcc,
596: unsigned int ncpus)
597: {
598: int mib[] = { CTL_KERN, KERN_POOL, KERN_POOL_CACHE_CPUS, pool };
599: size_t len = sizeof(*kpcc) * ncpus;
600:
601: return (sysctl(mib, nitems(mib), kpcc, &len, NULL, 0));
602: }
603:
604: static int
605: pool_get_name(int pool, char *name, size_t len)
606: {
607: int mib[] = { CTL_KERN, KERN_POOL, KERN_POOL_NAME, pool };
608:
609: return (sysctl(mib, nitems(mib), name, &len, NULL, 0));
610: }
611:
612: static int
613: hw_ncpusfound(void)
614: {
615: int mib[] = { CTL_HW, HW_NCPUFOUND };
616:
617: return (sysctl_rdint(mib, nitems(mib)));
618: }
619:
620: static int
621: sysctl_rdint(const int *mib, unsigned int nmib)
622: {
623: int i;
624: size_t size = sizeof(i);
625:
626: if (sysctl(mib, nmib, &i, &size, NULL, 0) == -1)
627: return (-1);
628:
629: return (i);
1.1 canacar 630: }