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, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright (c) 1994, by Sun Microsytems, Inc.
24 */
25
26 #pragma ident "%Z%%M% %I% %E% SMI"
27
28 /*
29 * Utility functions to initialize tnfctl handle, find functions that
30 * can be plugged into probes, find trace file information, and create
31 * a trace file for process tracing.
32 */
33
34 #ifndef DEBUG
35 #define NDEBUG 1
36 #endif
37
38 #include "tnfctl_int.h"
39 #include "dbg.h"
40
41 #include <assert.h>
42 #include <errno.h>
43 #include <stdio.h>
44 #include <string.h>
45 #include <unistd.h>
46 #include <stdlib.h>
47 #include <fcntl.h>
48 #include <sys/param.h>
49
50 #include "tnf_buf.h"
51 /*
52 * Defines - Project private interfaces in libtnfprobe.so
53 */
54
55 #define TRACEFILE_NAME "tnf_trace_file_name"
56 #define TRACEFILE_SIZE "tnf_trace_file_size"
57 #define TRACEFILE_MIN "tnf_trace_file_min"
58 #define TRACE_ERROR "_tnfw_b_control"
59
60 #define TRACE_ALLOC "tnf_trace_alloc"
61 #define TRACE_COMMIT "tnf_trace_commit"
62 #define TRACE_ROLLBACK "tnf_trace_rollback"
63 #define DEBUG_ENTRY "tnf_probe_debug"
64
65 #define PROBE_LIST_HEAD "__tnf_probe_list_head"
66 #define PROBE_LIST_VALID "__tnf_probe_list_valid"
67
68 #define NONTHREAD_TEST "tnf_non_threaded_test_addr"
69 #define THREAD_TEST "tnf_threaded_test_addr"
70 #define PROBE_THR_SYNC "__tnf_probe_thr_sync"
71
72 #define MEMSEG_PTR "__tnf_probe_memseg_p"
73
74 /* Project private interfaces in libthread.so */
75 #define LIBTHREAD_PRESENT "thr_probe_getfunc_addr"
76
77 /*
78 * Local declarations
79 */
80
81 static tnfctl_errcode_t find_test_func(tnfctl_handle_t *hndl);
82 static tnfctl_errcode_t find_target_syms(tnfctl_handle_t *hndl);
83 static tnfctl_errcode_t find_trace_file_info(tnfctl_handle_t *hndl);
84 static tnfctl_errcode_t check_trace_error(tnfctl_handle_t *hndl);
85
86 /*
87 * _tnfctl_refresh_process() - search for new shared objects. If any
88 * found, discover probes in new shared objects.
89 * NOT to be called in kernel mode.
90 */
91
92 tnfctl_errcode_t
_tnfctl_refresh_process(tnfctl_handle_t * hndl,boolean_t * lmap_ok,enum event_op_t * dl_evt)93 _tnfctl_refresh_process(tnfctl_handle_t *hndl, boolean_t *lmap_ok,
94 enum event_op_t *dl_evt)
95 {
96 tnfctl_errcode_t prexstat = TNFCTL_ERR_NONE;
97 boolean_t release_lock;
98
99 assert(hndl->mode != KERNEL_MODE);
100
101 /*LINTED statement has no consequent: else*/
102 LOCK(hndl, prexstat, release_lock);
103
104 prexstat = check_trace_error(hndl);
105 if (prexstat)
106 goto finish_func;
107
108 /*
109 * update the link map. caller decides what to do on
110 * inconsistent link map
111 */
112 prexstat = _tnfctl_lmap_update(hndl, lmap_ok, dl_evt);
113 if (prexstat)
114 goto finish_func;
115
116 /* link map is ok now */
117 prexstat = find_test_func(hndl);
118 if (prexstat)
119 goto finish_func;
120 if (*dl_evt != EVT_NONE) {
121 prexstat = _tnfctl_find_all_probes(hndl);
122 if (prexstat)
123 goto finish_func;
124 }
125
126 finish_func:
127 /*LINTED statement has no consequent: else*/
128 UNLOCK(hndl, release_lock);
129
130 return (prexstat);
131 }
132
133 /*
134 * initialize tnfctl handle for a new target
135 */
136 tnfctl_errcode_t
_tnfctl_set_state(tnfctl_handle_t * hndl)137 _tnfctl_set_state(tnfctl_handle_t *hndl)
138 {
139 tnfctl_errcode_t prexstat = TNFCTL_ERR_NONE;
140 boolean_t lmap_ok;
141 enum event_op_t dl_evt;
142 boolean_t release_lock;
143
144 hndl->targ_pid = hndl->p_getpid(hndl->proc_p);
145
146 /*LINTED statement has no consequent: else*/
147 LOCK(hndl, prexstat, release_lock);
148
149 /*
150 * initialize the link map table. If link map is not ok, it is an
151 * error.
152 */
153 prexstat = _tnfctl_lmap_update(hndl, &lmap_ok, &dl_evt);
154 if (prexstat)
155 goto end_func;
156
157 /* find the needed target symbols */
158 prexstat = find_target_syms(hndl);
159 if (prexstat) {
160 /* is libtnfprobe.so loaded in target ? */
161 goto end_func;
162 }
163
164 prexstat = find_trace_file_info(hndl);
165 if (prexstat)
166 goto end_func;
167
168 prexstat = find_test_func(hndl);
169 if (prexstat)
170 goto end_func;
171
172 prexstat = _tnfctl_find_all_probes(hndl);
173 if (prexstat)
174 goto end_func;
175
176 prexstat = check_trace_error(hndl);
177 /* fall into end_func */
178
179 end_func:
180 /*LINTED statement has no consequent: else*/
181 UNLOCK(hndl, release_lock);
182
183 return (prexstat);
184 }
185
186 /*
187 * find the test function for a probe. The test function could change
188 * with time, so we have to repeatedly check for the test function to use
189 */
190 static tnfctl_errcode_t
find_test_func(tnfctl_handle_t * hndl)191 find_test_func(tnfctl_handle_t *hndl)
192 {
193 long thr_sync;
194 int miscstat;
195
196 if (hndl->mt_target == B_FALSE) {
197 /* no libthread linked in */
198 hndl->testfunc = hndl->nonthread_test;
199 } else {
200 /*
201 * check whether libthread/libtnfw have synced up.
202 * If not yet synced up, use non-threaded test function
203 */
204
205 /* assume we are going to use threaded test */
206 hndl->testfunc = hndl->thread_test;
207 miscstat = hndl->p_read(hndl->proc_p, hndl->thread_sync,
208 &thr_sync, sizeof (thr_sync));
209 if (miscstat != 0)
210 return (TNFCTL_ERR_INTERNAL);
211 /* if not yet synced up, change test func to non-threaded one */
212 if (thr_sync == 0) {
213 hndl->testfunc = hndl->nonthread_test;
214 }
215 }
216
217 /*
218 * Note: the testfunc in the target can change underneath us because
219 * in an MT program the init section of libthread changes all the
220 * test functions from the non-threaded one to the threaded one.
221 * So, every time we write out a probe, we have to make sure that
222 * we are using the correct test function by not trusting the test
223 * function in our copy of the probe. A more fool-proof solution
224 * which will allow other fields in the probe to change internally
225 * is to refresh every probe on a _tnfctl_refresh_process()
226 */
227 return (TNFCTL_ERR_NONE);
228 }
229
230 /*
231 * check_trace_error() - checks whether there was an error in tracing
232 * side effects trace_buf_state and trace_state in hndl
233 * note: call this function only after trace_file_name is set up
234 * in hndl
235 */
236 tnfctl_errcode_t
check_trace_error(tnfctl_handle_t * hndl)237 check_trace_error(tnfctl_handle_t *hndl)
238 {
239 int miscstat;
240 uintptr_t trace_error_ptr;
241 TNFW_B_CONTROL trace_error_rec;
242
243 /* read in the value of the control structure pointer */
244 miscstat = hndl->p_read(hndl->proc_p, hndl->trace_error,
245 &trace_error_ptr, sizeof (trace_error_ptr));
246 if (miscstat != 0)
247 return (TNFCTL_ERR_INTERNAL);
248
249 /* read in the value of the control structure */
250 miscstat = hndl->p_read(hndl->proc_p, trace_error_ptr, &trace_error_rec,
251 sizeof (trace_error_rec));
252 if (miscstat != 0)
253 return (TNFCTL_ERR_INTERNAL);
254
255 if (trace_error_rec.tnf_state == TNFW_B_NOBUFFER) {
256 /*
257 * massage into correct state for caller - the target might
258 * not have hit the first probe and hence we got "no buffer".
259 * So, if the user had given a file name, return BUF_OK.
260 */
261 if (hndl->trace_file_name == NULL)
262 hndl->trace_buf_state = TNFCTL_BUF_NONE;
263 else
264 hndl->trace_buf_state = TNFCTL_BUF_OK;
265 } else if (trace_error_rec.tnf_state == TNFW_B_BROKEN) {
266 hndl->trace_buf_state = TNFCTL_BUF_BROKEN;
267 } else {
268 hndl->trace_buf_state = TNFCTL_BUF_OK;
269 }
270
271 if (TNFW_B_IS_STOPPED(trace_error_rec.tnf_state))
272 hndl->trace_state = B_FALSE;
273 else
274 hndl->trace_state = B_TRUE;
275
276 return (TNFCTL_ERR_NONE);
277
278 } /* end find_alloc_func */
279
280 /*
281 * find_target_syms() - finds needed target functions
282 * sideffects allocfunc, commitfunc, endfunc, rollbackfunc in hndl
283 */
284 static tnfctl_errcode_t
find_target_syms(tnfctl_handle_t * hndl)285 find_target_syms(tnfctl_handle_t *hndl)
286 {
287 tnfctl_errcode_t prexstat;
288 uintptr_t temp_addr;
289 int miscstat;
290
291 prexstat = _tnfctl_sym_find(hndl, TRACE_ALLOC, &hndl->allocfunc);
292 if (prexstat)
293 goto end_of_func;
294
295 prexstat = _tnfctl_sym_find(hndl, TRACE_COMMIT, &hndl->commitfunc);
296 if (prexstat)
297 goto end_of_func;
298
299 prexstat = _tnfctl_sym_find(hndl, TRACE_END_FUNC, &hndl->endfunc);
300 if (prexstat)
301 goto end_of_func;
302
303 prexstat = _tnfctl_sym_find(hndl, TRACE_ROLLBACK, &hndl->rollbackfunc);
304 if (prexstat)
305 goto end_of_func;
306
307 prexstat = _tnfctl_sym_find(hndl, PROBE_LIST_HEAD,
308 &hndl->probelist_head);
309 if (prexstat)
310 goto end_of_func;
311
312 prexstat = _tnfctl_sym_find(hndl, TRACE_ERROR, &hndl->trace_error);
313 if (prexstat)
314 goto end_of_func;
315
316 prexstat = _tnfctl_sym_find(hndl, MEMSEG_PTR, &temp_addr);
317 if (prexstat)
318 goto end_of_func;
319
320 /* dereference to get the actual address of structure */
321 miscstat = hndl->p_read(hndl->proc_p, temp_addr, &hndl->memseg_p,
322 sizeof (hndl->memseg_p));
323 if (miscstat != 0)
324 return (TNFCTL_ERR_INTERNAL);
325
326 prexstat = _tnfctl_sym_find(hndl, PROBE_LIST_VALID,
327 &hndl->probelist_valid);
328 if (prexstat)
329 goto end_of_func;
330
331 prexstat = _tnfctl_sym_find(hndl, NONTHREAD_TEST, &temp_addr);
332 if (prexstat)
333 goto end_of_func;
334
335 /* dereference to get the actual function address */
336 miscstat = hndl->p_read(hndl->proc_p, temp_addr, &hndl->nonthread_test,
337 sizeof (hndl->nonthread_test));
338 if (miscstat != 0)
339 return (TNFCTL_ERR_INTERNAL);
340
341 prexstat = _tnfctl_sym_find(hndl, THREAD_TEST, &temp_addr);
342 if (prexstat)
343 goto end_of_func;
344
345 /* dereference to get the actual function address */
346 miscstat = hndl->p_read(hndl->proc_p, temp_addr, &hndl->thread_test,
347 sizeof (hndl->thread_test));
348 if (miscstat != 0)
349 return (TNFCTL_ERR_INTERNAL);
350
351 prexstat = _tnfctl_sym_find(hndl, PROBE_THR_SYNC, &hndl->thread_sync);
352 if (prexstat)
353 goto end_of_func;
354
355 prexstat = _tnfctl_sym_find(hndl, LIBTHREAD_PRESENT, &temp_addr);
356 if (prexstat) {
357 if (prexstat == TNFCTL_ERR_BADARG) {
358 /* no libthread linked in */
359 hndl->mt_target = B_FALSE;
360 /* this is not an error condition */
361 prexstat = TNFCTL_ERR_NONE;
362 } else {
363 return (prexstat);
364 }
365 } else {
366 hndl->mt_target = B_TRUE;
367 }
368
369 end_of_func:
370 if (prexstat == TNFCTL_ERR_BADARG)
371 prexstat = TNFCTL_ERR_NOLIBTNFPROBE;
372
373 return (prexstat);
374 }
375
376 /*
377 * _tnfctl_create_tracefile() - initializes tracefile, sets the tracefile name
378 * and size
379 * side effects trace_file_name and trace_buf_size in hndl
380 */
381
382 #define ZBUFSZ (64 * 1024)
383
384 tnfctl_errcode_t
_tnfctl_create_tracefile(tnfctl_handle_t * hndl,const char * trace_file_name,uint_t trace_file_size)385 _tnfctl_create_tracefile(tnfctl_handle_t *hndl, const char *trace_file_name,
386 uint_t trace_file_size)
387 {
388 char *preexisting;
389 tnfctl_errcode_t prexstat;
390 int miscstat;
391 char path[MAXPATHLEN];
392 uintptr_t name_addr, size_addr;
393 uint_t outsize;
394 char zerobuf[ZBUFSZ];
395 int fd, sz, i;
396
397 /* find the neccessary symbols in the target */
398 prexstat = _tnfctl_sym_find(hndl, TRACEFILE_NAME, &name_addr);
399 if (prexstat) {
400 if (prexstat == TNFCTL_ERR_BADARG)
401 prexstat = TNFCTL_ERR_INTERNAL;
402 return (prexstat);
403 }
404 prexstat = _tnfctl_sym_find(hndl, TRACEFILE_SIZE, &size_addr);
405 if (prexstat) {
406 if (prexstat == TNFCTL_ERR_BADARG)
407 prexstat = TNFCTL_ERR_INTERNAL;
408 return (prexstat);
409 }
410
411 /* Double check that a file name doesn't already exist */
412 preexisting = NULL;
413 prexstat = _tnfctl_readstr_targ(hndl, name_addr, &preexisting);
414 if (prexstat) {
415 if (preexisting)
416 free(preexisting);
417 return (prexstat);
418 }
419
420 /* There better not be a file name there yet */
421 assert(preexisting[0] == '\0');
422
423 /* paranoia - for optimized compilation */
424 if (preexisting[0] != '\0')
425 return (TNFCTL_ERR_BUFEXISTS);
426
427 /* free memory in preexisting string */
428 if (preexisting)
429 free(preexisting);
430
431 if (trace_file_size < hndl->trace_min_size) {
432 return (TNFCTL_ERR_SIZETOOSMALL);
433 }
434
435 /* do we have an absolute, relative or no pathname specified? */
436 if (trace_file_name == NULL) {
437 return (TNFCTL_ERR_BADARG);
438 }
439 if (trace_file_name[0] == '/') {
440 /* absolute path to tracefile specified */
441 if ((strlen(trace_file_name) + 1) > (size_t) MAXPATHLEN) {
442 /* directory specification too long */
443 return (TNFCTL_ERR_BADARG);
444 }
445 (void) strcpy(path, trace_file_name);
446 } else {
447 char *cwd;
448
449 /* relative path to tracefile specified */
450 cwd = getcwd(NULL, MAXPATHLEN);
451 if (!cwd) {
452 return (tnfctl_status_map(errno));
453 }
454 if ((strlen(cwd) + 1 + strlen(trace_file_name) + 1) >
455 (size_t) MAXPATHLEN) {
456 /* path name too long */
457 return (TNFCTL_ERR_BADARG);
458 }
459 (void) sprintf(path, "%s/%s", cwd, trace_file_name);
460
461 free(cwd);
462 }
463
464 outsize = trace_file_size;
465
466 DBG_TNF_PROBE_2(_tnfctl_create_tracefile_1, "libtnfctl",
467 "sunw%verbosity 1; sunw%debug 'setting trace file name'",
468 tnf_string, tracefile_name, path,
469 tnf_long, tracefile_size, outsize);
470
471 /* unlink a previous tracefile (if one exists) */
472 (void) unlink(path);
473
474 /* create the new tracefile */
475 fd = open(path, O_CREAT | O_RDWR | O_TRUNC, 0644);
476 if (fd < 0) {
477 return (tnfctl_status_map(errno));
478 }
479
480 /* zero fill the file */
481 (void) memset(zerobuf, 0, ZBUFSZ);
482 sz = ZBUFSZ;
483 for (i = 0; i < outsize; i += sz) {
484 ulong_t retval;
485
486 sz = ((outsize - i) > ZBUFSZ) ? ZBUFSZ : (outsize - i);
487 retval = write(fd, zerobuf, sz);
488 if (retval != sz) {
489 /* trouble zeroing tracefile */
490 return (tnfctl_status_map(errno));
491 }
492 }
493
494 /* close the file */
495 (void) close(fd);
496
497 /* write the tracefile name and size into the target process */
498 miscstat = hndl->p_write(hndl->proc_p, name_addr, path,
499 strlen(path) + 1);
500 if (miscstat != 0)
501 return (TNFCTL_ERR_INTERNAL);
502 miscstat = hndl->p_write(hndl->proc_p, size_addr, &outsize,
503 sizeof (outsize));
504 if (miscstat != 0)
505 return (TNFCTL_ERR_INTERNAL);
506
507 hndl->trace_file_name = strdup(path);
508 if (hndl->trace_file_name == NULL)
509 return (TNFCTL_ERR_ALLOCFAIL);
510 hndl->trace_buf_size = outsize;
511 hndl->trace_buf_state = TNFCTL_BUF_OK;
512 return (TNFCTL_ERR_NONE);
513 } /* end _tnfctl_create_tracefile */
514
515 /*
516 * find_trace_file_info()
517 * finds out information about the trace file.
518 * side effects trace_buf_size, trace_min_size, trace_file_name in hndl
519 */
520
521 static tnfctl_errcode_t
find_trace_file_info(tnfctl_handle_t * hndl)522 find_trace_file_info(tnfctl_handle_t *hndl)
523 {
524 tnfctl_errcode_t prexstat;
525 int miscstat;
526 char *preexisting;
527 uintptr_t name_addr, size_addr, min_addr;
528 uint_t outsize, minoutsize;
529
530 /* find the neccessary symbols in the target */
531 prexstat = _tnfctl_sym_find(hndl, TRACEFILE_NAME, &name_addr);
532 if (prexstat) {
533 if (prexstat == TNFCTL_ERR_BADARG)
534 prexstat = TNFCTL_ERR_INTERNAL;
535 return (prexstat);
536 }
537 prexstat = _tnfctl_sym_find(hndl, TRACEFILE_SIZE, &size_addr);
538 if (prexstat) {
539 if (prexstat == TNFCTL_ERR_BADARG)
540 prexstat = TNFCTL_ERR_INTERNAL;
541 return (prexstat);
542 }
543 prexstat = _tnfctl_sym_find(hndl, TRACEFILE_MIN, &min_addr);
544 if (prexstat) {
545 if (prexstat == TNFCTL_ERR_BADARG)
546 prexstat = TNFCTL_ERR_INTERNAL;
547 return (prexstat);
548 }
549
550 /* read file name */
551 preexisting = NULL;
552 prexstat = _tnfctl_readstr_targ(hndl, name_addr, &preexisting);
553 if (prexstat) {
554 if (preexisting)
555 free(preexisting);
556 return (prexstat);
557 }
558
559 /* read the minimum file size from the target */
560 miscstat = hndl->p_read(hndl->proc_p, min_addr, &minoutsize,
561 sizeof (minoutsize));
562 if (miscstat != 0)
563 return (TNFCTL_ERR_INTERNAL);
564 hndl->trace_min_size = minoutsize;
565
566 /* if there is no filename, we are done */
567 if (preexisting[0] == '\0') {
568 hndl->trace_file_name = NULL;
569 hndl->trace_buf_size = 0;
570 } else {
571 hndl->trace_file_name = preexisting;
572 /* read size of file */
573 miscstat = hndl->p_read(hndl->proc_p, size_addr,
574 &outsize, sizeof (outsize));
575 if (miscstat != 0)
576 return (TNFCTL_ERR_INTERNAL);
577 hndl->trace_buf_size = outsize;
578 }
579
580 return (TNFCTL_ERR_NONE);
581 } /* end find_trace_file_info */
582
583 /*
584 * wrapper functions over native /proc functions implemented by proc
585 * layer
586 */
587 int
_tnfctl_read_targ(void * proc_p,uintptr_t addr,void * buf,size_t size)588 _tnfctl_read_targ(void *proc_p, uintptr_t addr, void *buf, size_t size)
589 {
590 return (prb_proc_read(proc_p, addr, buf, size));
591 }
592
593 int
_tnfctl_write_targ(void * proc_p,uintptr_t addr,void * buf,size_t size)594 _tnfctl_write_targ(void *proc_p, uintptr_t addr, void *buf, size_t size)
595 {
596 return (prb_proc_write(proc_p, addr, buf, size));
597 }
598
599 int
_tnfctl_loadobj_iter(void * proc_p,tnfctl_ind_obj_f * func,void * client_data)600 _tnfctl_loadobj_iter(void *proc_p, tnfctl_ind_obj_f *func, void *client_data)
601 {
602 prb_loadobj_f *same_func = (prb_loadobj_f *) func;
603
604 return (prb_loadobj_iter(proc_p, same_func, client_data));
605 }
606
607 pid_t
_tnfctl_pid_get(void * proc_p)608 _tnfctl_pid_get(void *proc_p)
609 {
610 return (prb_proc_pid_get(proc_p));
611 }
612
613 /*
614 * _tnfctl_readstr_targ() - dereferences a string in the target
615 * NOTE: There is a similar routine called prb_proc_readstr()
616 * used by proc layer. It would be better if there was only
617 * one of these functions defined.
618 */
619
620 #define BUFSZ 256
621
622 tnfctl_errcode_t
_tnfctl_readstr_targ(tnfctl_handle_t * hndl,uintptr_t addr,char ** outstr_pp)623 _tnfctl_readstr_targ(tnfctl_handle_t *hndl, uintptr_t addr, char **outstr_pp)
624 {
625 int retstat;
626 int bufsz = BUFSZ;
627 char buffer[BUFSZ + 1];
628 offset_t offset;
629 char *ptr, *orig_ptr;
630
631 *outstr_pp = NULL;
632 offset = 0;
633
634 /* allocate an inital return buffer */
635 ptr = (char *) malloc(BUFSZ);
636 if (!ptr) {
637 DBG((void) fprintf(stderr,
638 "_tnfctl_readstr_targ: malloc failed\n"));
639 return (TNFCTL_ERR_ALLOCFAIL);
640 }
641 /*LINTED constant in conditional context*/
642 while (1) {
643 int i;
644
645 /* read a chunk into our buffer */
646 retstat = hndl->p_read(hndl->proc_p, addr + offset, buffer,
647 bufsz);
648 if (retstat != 0) {
649
650 /*
651 * if we get into trouble with a large read, try again
652 * with a single byte. Subsequent failiure is real ...
653 */
654 if (bufsz > 1) {
655 bufsz = 1;
656 continue;
657 }
658
659 DBG((void) fprintf(stderr,
660 "_tnfctl_readstr_targ: target read failed: \n"));
661 free(ptr);
662 return (TNFCTL_ERR_INTERNAL);
663 }
664 /* copy the chracters into the return buffer */
665 for (i = 0; i < bufsz; i++) {
666 char c = buffer[i];
667
668 ptr[offset + i] = c;
669 if (c == '\0') {
670 /* hooray! we saw the end of the string */
671 *outstr_pp = ptr;
672 return (TNFCTL_ERR_NONE);
673 }
674 }
675
676 /* bummer, need to grab another bufsz characters */
677 offset += bufsz;
678 orig_ptr = ptr;
679 ptr = (char *) realloc(ptr, offset + bufsz);
680 if (!ptr) {
681 free(orig_ptr);
682 DBG((void) fprintf(stderr,
683 "_tnfctl_readstr_targ: realloc failed\n"));
684 return (TNFCTL_ERR_ALLOCFAIL);
685 }
686 }
687
688 #if defined(lint)
689 return (TNFCTL_ERR_NONE);
690 #endif
691
692 }
693