18cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd/** 28cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * @file opd_parse_proc.c 38cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * Parsing of /proc/#pid 48cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * 58cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * @remark Copyright 2002 OProfile authors 68cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * @remark Read the file COPYING 78cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * 88cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * @author John Levon 98cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * @author Philippe Elie 108cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd */ 118cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 128cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd#include "op_libiberty.h" 138cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 148cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd#include "opd_parse_proc.h" 158cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd#include "opd_proc.h" 168cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd#include "opd_mapping.h" 178cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd#include "opd_image.h" 188cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd#include "opd_printf.h" 198cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 208cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd#include "op_file.h" 218cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd#include "op_fileio.h" 228cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 238cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd#include <dirent.h> 248cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd#include <stdio.h> 258cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd#include <stdlib.h> 268cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd#include <string.h> 278cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 288cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd/** 298cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * opd_add_ascii_map - parse an ASCII map string for a process 308cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * @param proc process to add map to 318cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * @param line 0-terminated ASCII string 328cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * @param image_name the binary application name 338cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * 348cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * Attempt to parse the string @line for map information 358cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * and add the info to the process @proc. Returns %1 368cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * on success, %0 otherwise. 378cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * 388cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * The parsing is based on Linux 2.4 format, which looks like this : 398cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * 408cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * 4001e000-400fc000 r-xp 00000000 03:04 31011 /lib/libc-2.1.2.so 418cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd */ 428cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd/* FIXME: handle (deleted) */ 438cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Doddstatic int opd_add_ascii_map(struct opd_proc * proc, char const * line, 448cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd char * const image_name) 458cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd{ 468cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd unsigned long offset, start, end; 478cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd struct opd_image * image; 488cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd char const * cp = line; 498cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 508cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd /* skip to protection field */ 518cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd while (*cp && *cp != ' ') 528cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd cp++; 538cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 548cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd /* handle rwx */ 558cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd if (!*cp || (!*(++cp)) || (!*(++cp)) || (*(++cp) != 'x')) 568cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd return 0; 578cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 588cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd /* get start and end from "40000000-4001f000" */ 598cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd if (sscanf(line, "%lx-%lx", &start, &end) != 2) 608cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd return 0; 618cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 628cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd /* "p " */ 638cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd cp += 2; 648cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 658cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd /* read offset */ 668cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd if (sscanf(cp, "%lx", &offset) != 1) 678cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd return 0; 688cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 698cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd while (*cp && *cp != '/') 708cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd cp++; 718cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 728cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd if (!*cp) 738cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd return 0; 748cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 758cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd image = opd_get_image(cp, image_name, 0, proc->tid, proc->tgid); 768cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd if (!image) 778cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd return 0; 788cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 798cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd opd_add_mapping(proc, image, start, offset, end); 808cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 818cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd return 1; 828cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd} 838cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 848cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 858cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd/** 868cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * opd_get_ascii_maps - read all maps for a process 878cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * @param proc process to work on 888cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * 898cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * Read the /proc/<pid>/maps file and add all 908cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * mapping information found to the process @proc. 918cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd */ 928cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Doddstatic void opd_get_ascii_maps(struct opd_proc * proc) 938cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd{ 948cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd FILE * fp; 958cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd char mapsfile[20] = "/proc/"; 968cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd char * line; 978cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd char exe_name[20]; 988cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd char * image_name; 998cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd struct list_head * pos; 1008cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 1018cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd snprintf(mapsfile + 6, 6, "%hu", proc->tid); 1028cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 1038cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd strcpy(exe_name, mapsfile); 1048cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 1058cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd strcat(mapsfile, "/maps"); 1068cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 1078cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd fp = op_try_open_file(mapsfile, "r"); 1088cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd if (!fp) 1098cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd return; 1108cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 1118cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd strcat(exe_name, "/exe"); 1128cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd image_name = xmalloc(PATH_MAX); 1138cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd if (!realpath(exe_name, image_name)) 1148cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd /* kernel thread are invalid symlink */ 1158cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd strcpy(image_name, exe_name); 1168cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 1178cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd verbprintf(vmisc, "image name %s for pid %u %u\n", image_name, proc->tid, proc->tgid); 1188cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 1198cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd while (1) { 1208cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd line = op_get_line(fp); 1218cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd if (!line) 1228cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd break; 1238cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 1248cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd opd_add_ascii_map(proc, line, image_name); 1258cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd free(line); 1268cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd } 1278cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 1288cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd /* dae assume than the first map added is the primary image name, this 1298cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * is always true at exec time but not for /proc/pid so restore 1308cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * the primary image name 1318cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd */ 1328cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd list_for_each(pos, &proc->maps) { 1338cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd struct opd_map * map = list_entry(pos, struct opd_map, next); 1348cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd if (!strcmp(map->image->name, image_name)) { 1358cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd if (pos != proc->maps.next) { 1368cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd fprintf(stderr, "swap map for image %s from %s to %s\n", image_name, proc->name, map->image->name); 1378cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd free((char *)proc->name); 1388cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd proc->name = xstrdup(map->image->name); 1398cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd } 1408cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd break; 1418cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd } 1428cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd } 1438cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 1448cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd if (list_empty(&proc->maps)) { 1458cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd /* we always need a valid proc->maps[0], we artificially give 1468cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * a map of length zero so on no samples will never go to this 1478cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * map. This is used only with --separate-samples and kernel 1488cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * thread when adding vmlinux and module maps to proc->maps[] 1498cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd */ 1508cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd /* FIXME: use the first field of /proc/pid/status as proc name 1518cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * for now we use /proc/%pid/exe as name */ 1528cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd struct opd_image * image = opd_get_image(image_name, 1538cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd image_name, 0, proc->tid, proc->tgid); 1548cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd if (image) 1558cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd opd_add_mapping(proc, image, 0, 0, 0); 1568cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd } 1578cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 1588cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd if (image_name) 1598cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd free(image_name); 1608cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 1618cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd op_close_file(fp); 1628cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd} 1638cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 1648cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 1658cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Doddstatic u32 read_tgid(u32 tid) 1668cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd{ 1678cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd char status_file[30] = "/proc/"; 1688cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd char * line; 1698cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd FILE * fp; 1708cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd u32 tgid; 1718cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 1728cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd snprintf(status_file + 6, 6, "%hu", tid); 1738cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 1748cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd strcat(status_file, "/status"); 1758cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 1768cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd fp = op_try_open_file(status_file, "r"); 1778cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd if (!fp) 1788cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd return 0; 1798cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 1808cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd while (1) { 1818cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd line = op_get_line(fp); 1828cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd if (!line) 1838cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd break; 1848cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 1858cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd if (sscanf(line, "Tgid: %u", &tgid) == 1) { 1868cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd free(line); 1878cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd op_close_file(fp); 1888cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd return tgid; 1898cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd } 1908cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd free(line); 1918cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd } 1928cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 1938cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd op_close_file(fp); 1948cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 1958cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd return 0; 1968cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd} 1978cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 1988cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 1998cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Doddvoid opd_get_ascii_procs(void) 2008cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd{ 2018cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd DIR * dir; 2028cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd struct dirent * dirent; 2038cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd struct opd_proc * proc; 2048cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd u32 pid; 2058cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 2068cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd if (!(dir = opendir("/proc"))) { 2078cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd perror("oprofiled: /proc directory could not be opened. "); 2088cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd exit(EXIT_FAILURE); 2098cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd } 2108cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 2118cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd while ((dirent = readdir(dir))) { 2128cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd if (sscanf(dirent->d_name, "%u", &pid) == 1) { 2138cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd u32 tgid = read_tgid(pid); 2148cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd verbprintf(vmisc, "ASCII added %u %u\n", pid, tgid); 2158cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd proc = opd_get_proc(pid, tgid); 2168cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd if (!proc) 2178cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd proc = opd_new_proc(pid, tgid); 2188cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd opd_get_ascii_maps(proc); 2198cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd } 2208cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd } 2218cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 2228cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd closedir(dir); 2238cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd} 224