]> git.saurik.com Git - apple/system_cmds.git/blame - lsmp.tproj/port_details.c
system_cmds-735.20.1.tar.gz
[apple/system_cmds.git] / lsmp.tproj / port_details.c
CommitLineData
45bc9d15 1/*
cf37c299 2 * Copyright (c) 2002-2016 Apple Inc. All rights reserved.
45bc9d15
A
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
1a7e3f61 22
45bc9d15 23#include <stdio.h>
1a7e3f61 24#include <unistd.h>
45bc9d15
A
25#include <stdlib.h>
26#include <libproc.h>
cf37c299 27#include <assert.h>
1a7e3f61
A
28#include <mach/mach.h>
29#include <mach/mach_voucher.h>
30#include "common.h"
45bc9d15 31
1a7e3f61 32const char * kobject_name(natural_t kotype)
45bc9d15
A
33{
34 switch (kotype) {
35 case IKOT_NONE: return "message-queue";
36 case IKOT_THREAD: return "THREAD";
37 case IKOT_TASK: return "TASK";
38 case IKOT_HOST: return "HOST";
39 case IKOT_HOST_PRIV: return "HOST-PRIV";
40 case IKOT_PROCESSOR: return "PROCESSOR";
41 case IKOT_PSET: return "PROCESSOR-SET";
42 case IKOT_PSET_NAME: return "PROCESSOR-SET-NAME";
43 case IKOT_TIMER: return "TIMER";
44 case IKOT_PAGING_REQUEST: return "PAGER-REQUEST";
45 case IKOT_MIG: return "MIG";
46 case IKOT_MEMORY_OBJECT: return "MEMORY-OBJECT";
47 case IKOT_XMM_PAGER: return "XMM-PAGER";
48 case IKOT_XMM_KERNEL: return "XMM-KERNEL";
49 case IKOT_XMM_REPLY: return "XMM-REPLY";
50 case IKOT_UND_REPLY: return "UND-REPLY";
51 case IKOT_HOST_NOTIFY: return "message-queue";
52 case IKOT_HOST_SECURITY: return "HOST-SECURITY";
53 case IKOT_LEDGER: return "LEDGER";
54 case IKOT_MASTER_DEVICE: return "MASTER-DEVICE";
55 case IKOT_TASK_NAME: return "TASK-NAME";
56 case IKOT_SUBSYSTEM: return "SUBSYSTEM";
57 case IKOT_IO_DONE_QUEUE: return "IO-QUEUE-DONE";
58 case IKOT_SEMAPHORE: return "SEMAPHORE";
59 case IKOT_LOCK_SET: return "LOCK-SET";
60 case IKOT_CLOCK: return "CLOCK";
61 case IKOT_CLOCK_CTRL: return "CLOCK-CONTROL";
62 case IKOT_IOKIT_SPARE: return "IOKIT-SPARE";
63 case IKOT_NAMED_ENTRY: return "NAMED-MEMORY";
64 case IKOT_IOKIT_CONNECT: return "IOKIT-CONNECT";
65 case IKOT_IOKIT_OBJECT: return "IOKIT-OBJECT";
66 case IKOT_UPL: return "UPL";
67 case IKOT_MEM_OBJ_CONTROL: return "XMM-CONTROL";
68 case IKOT_AU_SESSIONPORT: return "SESSIONPORT";
69 case IKOT_FILEPORT: return "FILEPORT";
70 case IKOT_LABELH: return "MACF-LABEL";
1a7e3f61
A
71 case IKOT_TASK_RESUME: return "TASK_RESUME";
72 case IKOT_VOUCHER: return "VOUCHER";
73 case IKOT_VOUCHER_ATTR_CONTROL: return "VOUCHER_ATTR_CONTROL";
45bc9d15
A
74 case IKOT_UNKNOWN:
75 default: return "UNKNOWN";
76 }
77}
78
1a7e3f61
A
79#define VOUCHER_DETAIL_PREFIX " "
80
81static const unsigned int voucher_contents_size = 8192;
82static uint8_t voucher_contents[voucher_contents_size];
83
84
ac27e6b4 85static uint32_t safesize (int len){
cf37c299 86 return (len > 0) ? len : 0;
ac27e6b4
A
87}
88
cf37c299 89uint32_t show_recipe_detail(mach_voucher_attr_recipe_t recipe, char *voucher_outstr, uint32_t maxlen) {
ac27e6b4
A
90 uint32_t len = 0;
91 len += safesize(snprintf(&voucher_outstr[len], maxlen - len, VOUCHER_DETAIL_PREFIX "Key: %u, ", recipe->key));
92 len += safesize(snprintf(&voucher_outstr[len], maxlen - len, "Command: %u, ", recipe->command));
93 len += safesize(snprintf(&voucher_outstr[len], maxlen - len, "Previous voucher: 0x%x, ", recipe->previous_voucher));
94 len += safesize(snprintf(&voucher_outstr[len], maxlen - len, "Content size: %u\n", recipe->content_size));
cf37c299 95
1a7e3f61
A
96 switch (recipe->key) {
97 case MACH_VOUCHER_ATTR_KEY_ATM:
ac27e6b4 98 len += safesize(snprintf(&voucher_outstr[len], maxlen - len, VOUCHER_DETAIL_PREFIX "ATM ID: %llu\n", *(uint64_t *)(uintptr_t)recipe->content));
1a7e3f61
A
99 break;
100 case MACH_VOUCHER_ATTR_KEY_IMPORTANCE:
ac27e6b4 101 len += safesize(snprintf(&voucher_outstr[len], maxlen - len, VOUCHER_DETAIL_PREFIX "IMPORTANCE INFO: %s\n", (char *)recipe->content));
1a7e3f61
A
102 break;
103 case MACH_VOUCHER_ATTR_KEY_BANK:
ac27e6b4 104 len += safesize(snprintf(&voucher_outstr[len], maxlen - len, VOUCHER_DETAIL_PREFIX "RESOURCE ACCOUNTING INFO: %s\n", (char *)recipe->content));
1a7e3f61
A
105 break;
106 default:
cf37c299 107 len += print_hex_data(&voucher_outstr[len], maxlen - len, VOUCHER_DETAIL_PREFIX, "Recipe Contents", (void *)recipe->content, MIN(recipe->content_size, lsmp_config.voucher_detail_length));
1a7e3f61
A
108 break;
109 }
ac27e6b4 110
cf37c299 111 return len;
1a7e3f61
A
112}
113
ac27e6b4
A
114
115char * copy_voucher_detail(mach_port_t task, mach_port_name_t voucher) {
1a7e3f61
A
116 unsigned int recipe_size = voucher_contents_size;
117 kern_return_t kr = KERN_SUCCESS;
118 bzero((void *)&voucher_contents[0], sizeof(voucher_contents));
119 unsigned v_kobject = 0;
120 unsigned v_kotype = 0;
ac27e6b4
A
121 uint32_t detail_maxlen = VOUCHER_DETAIL_MAXLEN;
122 char * voucher_outstr = (char *)malloc(detail_maxlen);
123 voucher_outstr[0] = '\0';
124 uint32_t plen = 0;
125
1a7e3f61
A
126 kr = mach_port_kernel_object( task,
127 voucher,
128 &v_kotype, (unsigned *)&v_kobject);
129 if (kr == KERN_SUCCESS && v_kotype == IKOT_VOUCHER ) {
ac27e6b4 130
1a7e3f61
A
131 kr = mach_voucher_debug_info(task, voucher,
132 (mach_voucher_attr_raw_recipe_array_t)&voucher_contents[0],
133 &recipe_size);
134 if (kr != KERN_SUCCESS && kr != KERN_NOT_SUPPORTED) {
ac27e6b4
A
135 plen += safesize(snprintf(&voucher_outstr[plen], detail_maxlen - plen, VOUCHER_DETAIL_PREFIX "Voucher: 0x%x Failed to get contents %s\n", v_kobject, mach_error_string(kr)));
136 return voucher_outstr;
1a7e3f61 137 }
ac27e6b4 138
1a7e3f61 139 if (recipe_size == 0) {
ac27e6b4
A
140 plen += safesize(snprintf(&voucher_outstr[plen], detail_maxlen - plen, VOUCHER_DETAIL_PREFIX "Voucher: 0x%x has no contents\n", v_kobject));
141 return voucher_outstr;
1a7e3f61 142 }
ac27e6b4
A
143
144 plen += safesize(snprintf(&voucher_outstr[plen], detail_maxlen - plen, VOUCHER_DETAIL_PREFIX "Voucher: 0x%x\n", v_kobject));
1a7e3f61
A
145 unsigned int used_size = 0;
146 mach_voucher_attr_recipe_t recipe = NULL;
147 while (recipe_size > used_size) {
148 recipe = (mach_voucher_attr_recipe_t)&voucher_contents[used_size];
149 if (recipe->key) {
cf37c299 150 plen += show_recipe_detail(recipe, &voucher_outstr[plen], detail_maxlen - plen);
1a7e3f61
A
151 }
152 used_size += sizeof(mach_voucher_attr_recipe_data_t) + recipe->content_size;
153 }
154 } else {
ac27e6b4 155 plen += safesize(snprintf(&voucher_outstr[plen], detail_maxlen - plen, VOUCHER_DETAIL_PREFIX "Invalid voucher: 0x%x\n", voucher));
45bc9d15 156 }
cf37c299 157
ac27e6b4 158 return voucher_outstr;
45bc9d15
A
159}
160
1a7e3f61
A
161void get_receive_port_context(task_t taskp, mach_port_name_t portname, mach_port_context_t *context) {
162 if (context == NULL) {
163 return;
164 }
cf37c299 165
1a7e3f61
A
166 kern_return_t ret;
167 ret = mach_port_get_context(taskp, portname, context);
168 if (ret != KERN_SUCCESS) {
169 fprintf(stderr, "mach_port_get_context(0x%08x) failed: %s\n",
170 portname,
171 mach_error_string(ret));
172 *context = (mach_port_context_t)0;
173 }
174 return;
175}
176
177int get_recieve_port_status(task_t taskp, mach_port_name_t portname, mach_port_info_ext_t *info){
45bc9d15
A
178 if (info == NULL) {
179 return -1;
180 }
181 mach_msg_type_number_t statusCnt;
182 kern_return_t ret;
183 statusCnt = MACH_PORT_INFO_EXT_COUNT;
184 ret = mach_port_get_attributes(taskp,
185 portname,
186 MACH_PORT_INFO_EXT,
187 (mach_port_info_t)info,
188 &statusCnt);
189 if (ret != KERN_SUCCESS) {
190 fprintf(stderr, "mach_port_get_attributes(0x%08x) failed: %s\n",
191 portname,
192 mach_error_string(ret));
193 return -1;
194 }
cf37c299 195
45bc9d15
A
196 return 0;
197}
198
1a7e3f61
A
199void show_task_mach_ports(my_per_task_info_t *taskinfo, uint32_t taskCount, my_per_task_info_t *allTaskInfos)
200{
201 int i, emptycount = 0, portsetcount = 0, sendcount = 0, receivecount = 0, sendoncecount = 0, deadcount = 0, dncount = 0, vouchercount = 0, pid;
45bc9d15
A
202 kern_return_t ret;
203 pid_for_task(taskinfo->task, &pid);
cf37c299 204
45bc9d15
A
205 printf(" name ipc-object rights flags boost reqs recv send sonce oref qlimit msgcount context identifier type\n");
206 printf("--------- ---------- ---------- -------- ----- ---- ----- ----- ----- ---- ------ -------- ------------------ ----------- ------------\n");
207 for (i = 0; i < taskinfo->tableCount; i++) {
208 int j, k;
45bc9d15
A
209 boolean_t send = FALSE;
210 boolean_t sendonce = FALSE;
211 boolean_t dnreq = FALSE;
212 int sendrights = 0;
213 unsigned int kotype = 0;
214 vm_offset_t kobject = (vm_offset_t)0;
cf37c299 215
45bc9d15
A
216 /* skip empty slots in the table */
217 if ((taskinfo->table[i].iin_type & MACH_PORT_TYPE_ALL_RIGHTS) == 0) {
218 emptycount++;
219 continue;
220 }
cf37c299 221
45bc9d15
A
222 if (taskinfo->table[i].iin_type == MACH_PORT_TYPE_PORT_SET) {
223 mach_port_name_array_t members;
224 mach_msg_type_number_t membersCnt;
cf37c299 225
45bc9d15
A
226 ret = mach_port_get_set_status(taskinfo->task,
227 taskinfo->table[i].iin_name,
228 &members, &membersCnt);
229 if (ret != KERN_SUCCESS) {
230 fprintf(stderr, "mach_port_get_set_status(0x%08x) failed: %s\n",
231 taskinfo->table[i].iin_name,
232 mach_error_string(ret));
233 continue;
234 }
1a7e3f61 235 printf("0x%08x 0x%08x port-set -------- --- 1 %d members\n",
45bc9d15
A
236 taskinfo->table[i].iin_name,
237 taskinfo->table[i].iin_object,
238 membersCnt);
239 /* get some info for each portset member */
240 for (j = 0; j < membersCnt; j++) {
241 for (k = 0; k < taskinfo->tableCount; k++) {
242 if (taskinfo->table[k].iin_name == members[j]) {
243 mach_port_info_ext_t info;
1a7e3f61
A
244 mach_port_status_t port_status;
245 mach_port_context_t port_context = (mach_port_context_t)0;
45bc9d15
A
246 if (0 != get_recieve_port_status(taskinfo->task, taskinfo->table[k].iin_name, &info)) {
247 bzero((void *)&info, sizeof(info));
248 }
1a7e3f61
A
249 port_status = info.mpie_status;
250 get_receive_port_context(taskinfo->task, taskinfo->table[k].iin_name, &port_context);
251 printf(" - 0x%08x %s --%s%s%s%s%s%s %5d %s%s%s %5d %5.0d %5.0d %s %6d %8d 0x%016llx 0x%08x (%d) %s\n",
45bc9d15
A
252 taskinfo->table[k].iin_object,
253 (taskinfo->table[k].iin_type & MACH_PORT_TYPE_SEND) ? "recv,send ":"recv ",
1a7e3f61
A
254 SHOW_PORT_STATUS_FLAGS(port_status.mps_flags),
255 info.mpie_boost_cnt,
45bc9d15
A
256 (taskinfo->table[k].iin_type & MACH_PORT_TYPE_DNREQUEST) ? "D" : "-",
257 (port_status.mps_nsrequest) ? "N" : "-",
258 (port_status.mps_pdrequest) ? "P" : "-",
259 1,
260 taskinfo->table[k].iin_urefs,
261 port_status.mps_sorights,
262 (port_status.mps_srights) ? "Y" : "N",
263 port_status.mps_qlimit,
264 port_status.mps_msgcount,
1a7e3f61 265 (uint64_t)port_context,
45bc9d15
A
266 taskinfo->table[k].iin_name,
267 pid,
268 taskinfo->processName);
269 break;
270 }
271 }
272 }
cf37c299 273
45bc9d15
A
274 ret = vm_deallocate(mach_task_self(), (vm_address_t)members,
275 membersCnt * sizeof(mach_port_name_t));
276 if (ret != KERN_SUCCESS) {
277 fprintf(stderr, "vm_deallocate() failed: %s\n",
278 mach_error_string(ret));
279 exit(1);
280 }
281 portsetcount++;
282 continue;
283 }
cf37c299 284
45bc9d15
A
285 if (taskinfo->table[i].iin_type & MACH_PORT_TYPE_SEND) {
286 send = TRUE;
287 sendrights = taskinfo->table[i].iin_urefs;
288 sendcount++;
289 }
cf37c299 290
45bc9d15
A
291 if (taskinfo->table[i].iin_type & MACH_PORT_TYPE_DNREQUEST) {
292 dnreq = TRUE;
293 dncount++;
294 }
cf37c299 295
45bc9d15
A
296 if (taskinfo->table[i].iin_type & MACH_PORT_TYPE_RECEIVE) {
297 mach_port_status_t status;
298 mach_port_info_ext_t info;
299 mach_port_context_t context = (mach_port_context_t)0;
cf37c299 300 struct k2n_table_node *k2nnode;
45bc9d15 301 ret = get_recieve_port_status(taskinfo->task, taskinfo->table[i].iin_name, &info);
1a7e3f61 302 get_receive_port_context(taskinfo->task, taskinfo->table[i].iin_name, &context);
45bc9d15
A
303 /* its ok to fail in fetching attributes */
304 if (ret < 0) {
305 continue;
306 }
1a7e3f61 307 status = info.mpie_status;
45bc9d15
A
308 printf("0x%08x 0x%08x %s --%s%s%s%s%s%s %5d %s%s%s %5d %5.0d %5.0d %s %6d %8d 0x%016llx \n",
309 taskinfo->table[i].iin_name,
310 taskinfo->table[i].iin_object,
311 (send) ? "recv,send ":"recv ",
312 SHOW_PORT_STATUS_FLAGS(status.mps_flags),
313 info.mpie_boost_cnt,
314 (dnreq) ? "D":"-",
315 (status.mps_nsrequest) ? "N":"-",
316 (status.mps_pdrequest) ? "P":"-",
317 1,
318 sendrights,
319 status.mps_sorights,
320 (status.mps_srights) ? "Y":"N",
321 status.mps_qlimit,
322 status.mps_msgcount,
323 (uint64_t)context);
324 receivecount++;
cf37c299 325
45bc9d15
A
326 /* show other rights (in this and other tasks) for the port */
327 for (j = 0; j < taskCount; j++) {
cf37c299
A
328 if (allTaskInfos[j].valid == FALSE)
329 continue;
330
331 k2nnode = k2n_table_lookup(allTaskInfos[j].k2ntable, taskinfo->table[i].iin_object);
332
333 while (k2nnode) {
334 if (k2nnode->info_name != &taskinfo->table[i]) {
335 assert(k2nnode->info_name->iin_object == taskinfo->table[i].iin_object);
336
337 printf(" + %s -------- %s%s%s %5d <- 0x%08x (%d) %s\n",
338 (k2nnode->info_name->iin_type & MACH_PORT_TYPE_SEND_ONCE) ?
339 "send-once " : "send ",
340 (k2nnode->info_name->iin_type & MACH_PORT_TYPE_DNREQUEST) ? "D" : "-",
341 "-",
342 "-",
343 k2nnode->info_name->iin_urefs,
344 k2nnode->info_name->iin_name,
345 allTaskInfos[j].pid,
346 allTaskInfos[j].processName);
347 }
348
349 k2nnode = k2n_table_lookup_next(k2nnode, k2nnode->info_name->iin_name);
45bc9d15
A
350 }
351 }
352 continue;
353 }
354 else if (taskinfo->table[i].iin_type & MACH_PORT_TYPE_DEAD_NAME)
355 {
1a7e3f61 356 printf("0x%08x 0x%08x dead-name -------- --- %5d \n",
45bc9d15
A
357 taskinfo->table[i].iin_name,
358 taskinfo->table[i].iin_object,
359 taskinfo->table[i].iin_urefs);
360 deadcount++;
361 continue;
362 }
cf37c299 363
45bc9d15
A
364 if (taskinfo->table[i].iin_type & MACH_PORT_TYPE_SEND_ONCE) {
365 sendonce = TRUE;
366 sendoncecount++;
367 }
cf37c299 368
1a7e3f61 369 printf("0x%08x 0x%08x %s -------- %s%s%s %5.0d ",
45bc9d15
A
370 taskinfo->table[i].iin_name,
371 taskinfo->table[i].iin_object,
372 (send) ? "send ":"send-once ",
373 (dnreq) ? "D":"-",
374 "-",
375 "-",
376 (send) ? sendrights : 0);
cf37c299 377
45bc9d15
A
378 /* converting to kobjects is not always supported */
379 ret = mach_port_kernel_object(taskinfo->task,
380 taskinfo->table[i].iin_name,
381 &kotype, (unsigned *)&kobject);
382 if (ret == KERN_SUCCESS && kotype != 0) {
1a7e3f61
A
383 printf(" 0x%08x %s", (natural_t)kobject, kobject_name(kotype));
384 if ((kotype == IKOT_TASK_RESUME) || (kotype == IKOT_TASK) || (kotype == IKOT_TASK_NAME)) {
385 if (taskinfo->task_kobject == kobject) {
386 /* neat little optimization since in most cases tasks have themselves in their ipc space */
387 printf(" SELF (%d) %s", taskinfo->pid, taskinfo->processName);
388 } else {
389 my_per_task_info_t * _found_task = get_taskinfo_by_kobject((natural_t)kobject);
390 printf(" (%d) %s", _found_task->pid, _found_task->processName);
391 }
392 }
cf37c299 393
1a7e3f61
A
394 printf("\n");
395 if (kotype == IKOT_VOUCHER) {
396 vouchercount++;
397 if (lsmp_config.show_voucher_details) {
ac27e6b4
A
398 char * detail = copy_voucher_detail(taskinfo->task, taskinfo->table[i].iin_name);
399 printf("%s\n", detail);
400 free(detail);
1a7e3f61
A
401 }
402 }
45bc9d15
A
403 continue;
404 }
cf37c299 405
1a7e3f61
A
406 /* not kobject - find the receive right holder */
407 my_per_task_info_t *recv_holder_taskinfo;
408 mach_port_name_t recv_name = MACH_PORT_NULL;
409 if (KERN_SUCCESS == get_taskinfo_of_receiver_by_send_right(&taskinfo->table[i], &recv_holder_taskinfo, &recv_name)) {
410 mach_port_status_t port_status;
411 mach_port_info_ext_t info;
412 mach_port_context_t port_context = (mach_port_context_t)0;
413 if (0 != get_recieve_port_status(recv_holder_taskinfo->task, recv_name, &info)) {
414 bzero((void *)&port_status, sizeof(port_status));
415 }
416 port_status = info.mpie_status;
417 get_receive_port_context(recv_holder_taskinfo->task, recv_name, &port_context);
418 printf(" -> %6d %8d 0x%016llx 0x%08x (%d) %s\n",
419 port_status.mps_qlimit,
420 port_status.mps_msgcount,
421 (uint64_t)port_context,
422 recv_name,
423 recv_holder_taskinfo->pid,
424 recv_holder_taskinfo->processName);
425
426 } else
45bc9d15 427 printf(" 0x00000000 (-) Unknown Process\n");
cf37c299 428
45bc9d15
A
429 }
430 printf("total = %d\n", taskinfo->tableCount + taskinfo->treeCount - emptycount);
431 printf("SEND = %d\n", sendcount);
432 printf("RECEIVE = %d\n", receivecount);
433 printf("SEND_ONCE = %d\n", sendoncecount);
434 printf("PORT_SET = %d\n", portsetcount);
435 printf("DEAD_NAME = %d\n", deadcount);
436 printf("DNREQUEST = %d\n", dncount);
cf37c299
A
437 printf("VOUCHERS = %d\n", vouchercount);
438
45bc9d15
A
439}
440
cf37c299 441uint32_t print_hex_data(char *outstr, size_t maxlen, char *prefix, char *desc, void *addr, int len) {
1a7e3f61
A
442 int i;
443 unsigned char buff[17];
444 unsigned char *pc = addr;
ac27e6b4 445 uint32_t plen = 0;
cf37c299 446
1a7e3f61 447 if (desc != NULL)
ac27e6b4 448 plen += safesize(snprintf(&outstr[len], maxlen - plen, "%s%s:\n", prefix, desc));
cf37c299 449
1a7e3f61 450 for (i = 0; i < len; i++) {
cf37c299 451
1a7e3f61
A
452 if ((i % 16) == 0) {
453 if (i != 0)
ac27e6b4 454 plen += safesize(snprintf(&outstr[len], maxlen - plen, " %s\n", buff));
cf37c299 455
ac27e6b4 456 plen += safesize(snprintf(&outstr[len], maxlen - plen, "%s %04x ", prefix, i));
1a7e3f61 457 }
cf37c299 458
ac27e6b4 459 plen += safesize(snprintf(&outstr[len], maxlen - plen, " %02x", pc[i]));
cf37c299 460
1a7e3f61
A
461 if ((pc[i] < 0x20) || (pc[i] > 0x7e))
462 buff[i % 16] = '.';
463 else
464 buff[i % 16] = pc[i];
465 buff[(i % 16) + 1] = '\0';
45bc9d15 466 }
cf37c299 467
1a7e3f61 468 while ((i % 16) != 0) {
ac27e6b4 469 plen += safesize(snprintf(&outstr[len], maxlen - plen, " "));
1a7e3f61 470 i++;
45bc9d15 471 }
cf37c299 472
ac27e6b4 473 plen += safesize(snprintf(&outstr[len], maxlen - plen, " %s\n", buff));
cf37c299
A
474
475 return plen;
45bc9d15 476}