static void ioctl_emulate_open(int fd, const char *dev_path) { libc_func(fclose, int, FILE *); FILE *f; static char ioctl_path[PATH_MAX]; struct ioctl_fd_info *fdinfo; if (strncmp(dev_path, "/dev/", 5) != 0) return; fdinfo = malloc(sizeof(struct ioctl_fd_info)); fdinfo->tree = NULL; fdinfo->last = NULL; fd_map_add(&ioctl_wrapped_fds, fd, fdinfo); /* check if we have an ioctl tree for this */ snprintf(ioctl_path, sizeof(ioctl_path), "%s/ioctl/%s", getenv("UMOCKDEV_DIR"), dev_path); f = fopen(ioctl_path, "r"); if (f == NULL) return; fdinfo->tree = ioctl_tree_read(f); _fclose(f); if (fdinfo->tree == NULL) { fprintf(stderr, "ERROR: libumockdev-preload: failed to load ioctl record file for %s: empty or invalid format?", dev_path); exit(1); } DBG(DBG_IOCTL, "ioctl_emulate_open fd %i (%s): loaded ioctl tree\n", fd, dev_path); }
static void script_start_record(int fd, const char *logname) { FILE *log; struct script_record_info *srinfo; if (fd_map_get(&script_recorded_fds, fd, NULL)) { fprintf(stderr, "script_record_open: internal error: fd %i is already being recorded\n", fd); abort(); } log = fopen(logname, "a"); if (log == NULL) { perror("umockdev: failed to open script record file"); exit(1); } /* if we have a previous record, make sure that we start a new line */ if (ftell(log) > 0) putc('\n', log); srinfo = malloc(sizeof(struct script_record_info)); srinfo->log = log; assert(clock_gettime(CLOCK_MONOTONIC, &srinfo->time) == 0); srinfo->op = 0; fd_map_add(&script_recorded_fds, fd, srinfo); }
static int netlink_socket(int domain, int type, int protocol) { libc_func(socket, int, int, int, int); int fd; const char *path = getenv("UMOCKDEV_DIR"); if (domain == AF_NETLINK && protocol == NETLINK_KOBJECT_UEVENT && path != NULL) { fd = _socket(AF_UNIX, type, 0); fd_map_add(&wrapped_netlink_sockets, fd, NULL); DBG(DBG_NETLINK, "testbed wrapped socket: intercepting netlink, fd %i\n", fd); return fd; } return UNHANDLED; }
/* read UMOCKDEV_SCRIPT_* environment variables and set up dev_logfile_map * according to it */ static void init_script_dev_logfile_map(void) { int i, dev; char varname[100]; const char *devname, *logname; char *endptr; script_dev_logfile_map_inited = 1; for (i = 0; 1; ++i) { snprintf(varname, sizeof(varname), "UMOCKDEV_SCRIPT_RECORD_FILE_%i", i); logname = getenv(varname); if (logname == NULL) break; snprintf(varname, sizeof(varname), "UMOCKDEV_SCRIPT_RECORD_DEV_%i", i); devname = getenv(varname); if (devname == NULL) { fprintf(stderr, "umockdev: $%s not set\n", varname); exit(1); } dev = strtol(devname, &endptr, 10); if (dev != 0 && *endptr == '\0') { /* if it's a number, then it is an rdev of a device */ DBG("init_script_dev_logfile_map: will record script of device %i:%i into %s\n", major(dev), minor(dev), logname); fd_map_add(&script_dev_logfile_map, dev, logname); } else { /* if it's a path, then we record a socket */ if (script_socket_logfile_len < MAX_SCRIPT_SOCKET_LOGFILE) { DBG("init_script_dev_logfile_map: will record script of socket %s into %s\n", devname, logname); script_socket_logfile[2*script_socket_logfile_len] = devname; script_socket_logfile[2*script_socket_logfile_len+1] = logname; script_socket_logfile_len++; } else { fprintf(stderr, "too many script sockets to record\n"); abort(); } } } }
static void script_start_record(int fd, const char *logname, const char *recording_path, enum script_record_format fmt) { FILE *log; struct script_record_info *srinfo; if (fd_map_get(&script_recorded_fds, fd, NULL)) { fprintf(stderr, "script_start_record: internal error: fd %i is already being recorded\n", fd); abort(); } log = fopen(logname, "a+"); if (log == NULL) { perror("umockdev: failed to open script record file"); exit(1); } /* if we have a previous record... */ fseek(log, 0, SEEK_END); if (ftell(log) > 0) { DBG(DBG_SCRIPT, "script_start_record: Appending to existing record of format %i for path %s\n", fmt, recording_path); /* ...and we're going to record the device name... */ if (recording_path) { /* ... ensure we're recording the same device... */ char *existing_device_path; char line[1000]; libc_func(fgets, char *, char *, int, FILE *); fseek(log, 0, SEEK_SET); while (_fgets(line, sizeof(line), log)) { switch (fmt) { case FMT_DEFAULT: /* Start by skipping any leading comments */ if (line[0] == '#') continue; if (sscanf(line, "d 0 %ms\n", &existing_device_path) == 1) { DBG(DBG_SCRIPT, "script_start_record: recording %s, existing device spec in record %s\n", recording_path, existing_device_path); /* We have an existing "d /dev/something" directive, check it matches */ if (strcmp(recording_path, existing_device_path) != 0) { fprintf(stderr, "umockdev: attempt to record two different devices to the same script recording\n"); exit(1); } free(existing_device_path); } // device specification must be on the first non-comment line break; case FMT_EVEMU: if (strncmp(line, "E: ", 3) == 0) break; if (sscanf(line, "# device %ms\n", &existing_device_path) == 1) { DBG(DBG_SCRIPT, "script_start_record evemu format: recording %s, existing device spec in record %s\n", recording_path, existing_device_path); /* We have an existing "/dev/something" directive, check it matches */ if (strcmp(recording_path, existing_device_path) != 0) { fprintf(stderr, "umockdev: attempt to record two different devices to the same evemu recording\n"); exit(1); } free(existing_device_path); } break; default: fprintf(stderr, "umockdev: unknown script format %i\n", fmt); abort(); } } fseek(log, 0, SEEK_END); } /* ...finally, make sure that we start a new line */ putc('\n', log); } else if (recording_path) { /* this is a new record, start by recording the device path */ DBG(DBG_SCRIPT, "script_start_record: Starting new record of format %i\n", fmt); switch (fmt) { case FMT_DEFAULT: fprintf(log, "d 0 %s\n", recording_path); break; case FMT_EVEMU: fprintf(log, "# EVEMU 1.2\n# device %s\n", recording_path); break; default: fprintf(stderr, "umockdev: unknown script format %i\n", fmt); abort(); } } srinfo = malloc(sizeof(struct script_record_info)); srinfo->log = log; assert(clock_gettime(CLOCK_MONOTONIC, &srinfo->time) == 0); srinfo->op = 0; srinfo->fmt = fmt; fd_map_add(&script_recorded_fds, fd, srinfo); }
/* read UMOCKDEV_SCRIPT_* environment variables and set up dev_logfile_map * according to it */ static void init_script_dev_logfile_map(void) { int i; dev_t dev; char varname[100]; const char *devname, *logname, *format; script_dev_logfile_map_inited = 1; for (i = 0; 1; ++i) { snprintf(varname, sizeof(varname), "UMOCKDEV_SCRIPT_RECORD_FILE_%i", i); logname = getenv(varname); if (logname == NULL) break; snprintf(varname, sizeof(varname), "UMOCKDEV_SCRIPT_RECORD_DEV_%i", i); devname = getenv(varname); if (devname == NULL) { fprintf(stderr, "umockdev: $%s not set\n", varname); exit(1); } snprintf(varname, sizeof(varname), "UMOCKDEV_SCRIPT_RECORD_FORMAT_%i", i); format = getenv(varname); if (format == NULL) { fprintf(stderr, "umockdev: $%s not set\n", varname); exit(1); } dev = parse_dev_t(devname, NULL, 0); if (dev != (dev_t) -1) { /* if it's a dev_t, we should record its path */ const char *devpath; snprintf(varname, sizeof(varname), "UMOCKDEV_SCRIPT_RECORD_DEVICE_PATH_%i", i); devpath = getenv(varname); if (devpath == NULL) { fprintf(stderr, "umockdev: $%s not set\n", varname); exit(1); } DBG(DBG_SCRIPT, "init_script_dev_logfile_map: will record script of device %i:%i into %s\n", major(dev), minor(dev), logname); fd_map_add(&script_dev_logfile_map, dev, logname); fd_map_add(&script_dev_devpath_map, dev, devpath); if (strcmp(format, "default") == 0) fd_map_add(&script_dev_format_map, dev, (void*) FMT_DEFAULT); else if (strcmp(format, "evemu") == 0) fd_map_add(&script_dev_format_map, dev, (void*) FMT_EVEMU); else { fprintf(stderr, "umockdev: unknown device script record format '%s'\n", format); exit(1); } } else { if (strcmp(format, "default") != 0) { fprintf(stderr, "umockdev: unknown socket script record format '%s'\n", format); exit(1); } /* if it's a path, then we record a socket */ if (script_socket_logfile_len < MAX_SCRIPT_SOCKET_LOGFILE) { DBG(DBG_SCRIPT, "init_script_dev_logfile_map: will record script of socket %s into %s\n", devname, logname); script_socket_logfile[2*script_socket_logfile_len] = devname; script_socket_logfile[2*script_socket_logfile_len+1] = logname; script_socket_logfile_len++; } else { fprintf(stderr, "too many script sockets to record\n"); abort(); } } } }