#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include "scull.h"
#define CDEV_NAME "/dev/scull"
#define MAX_CONCURRENCY 20
/* Command-line option for concurrency */
static int g_concurrency = 0;
static void usage(const char *cmd) {
printf("Usage: %s <command>\n"
"Commands:\n"
" p <int> Use <int> processes to concurrently consume data\n"
" MIN: 1, MAX: %d\n"
" h Print this message\n",
cmd, MAX_CONCURRENCY);
}
static int do_procs(int fd) {
int i, status, ret = 0;
pid_t pid;
ssize_t count; /* bytes returned by read(), or -1 on error */
int ELEMSZ; /* maximum message size, as reported by the driver */
char *buf = NULL;
ELEMSZ = ioctl(fd, SCULL_IOCGETELEMSZ, &ELEMSZ);
if (ELEMSZ < 0) {
return -1;
}
for (i = 0; i < g_concurrency; i++) {
pid = fork();
if (pid == 0) {
/* Allocate ELEMSZ bytes for the data plus one
* extra byte for the null terminator. */
buf = (char *)malloc((size_t)(ELEMSZ + 1));
if (buf == NULL) {
exit(EXIT_FAILURE);
}
/* Ask for up to the complete data of one queue element.
* This blocks in the driver until a message is available. */
count = read(fd, buf, (size_t)ELEMSZ);
if (count < 0) {
perror("read");
} else {
buf[count] = '\0';
printf("read: %s\n", buf);
}
free(buf);
exit(EXIT_SUCCESS);
} else if (pid < 0) {
perror("cannot fork more children");
ret = -1;
break;
}
}
while (i-- > 0) {
wait(&status);
}
return ret;
}
typedef int cmd_t;
static cmd_t parse_arguments(int argc, const char **argv) {
cmd_t cmd;
if(argc < 2) {
fprintf(stderr, "%s: Invalid number of arguments\n", argv[0]);
cmd = -1;
goto ret;
}
/* Parse command and optional int argument */
cmd = argv[1][0];
switch(cmd) {
case 'p':
if(argc < 3) {
fprintf(stderr, "%s: Missing concurrency\n", argv[0]);
cmd = -1;
break;
}
g_concurrency = atoi(argv[2]);
if(g_concurrency < 1 || g_concurrency > MAX_CONCURRENCY) {
fprintf(stderr, "%s: Invalid value (%d) for "
"concurrency\n",
argv[0], g_concurrency);
cmd = -1;
break;
}
break;
default:
fprintf(stderr, "%s: Invalid command\n", argv[0]);
cmd = -1;
}
ret:
if(cmd < 0 || cmd == 'h') {
usage(argv[0]);
exit((cmd == 'h')? EXIT_SUCCESS : EXIT_FAILURE);
}
return cmd;
}
static int do_op(int fd, cmd_t cmd) {
int ret;
switch(cmd) {
case 'p':
ret = do_procs(fd);
break;
default:
/* Should never occur */
abort();
ret = -1; /* Keep the compiler happy */
}
if(ret != 0)
perror("ioctl");
return ret;
}
int main(int argc, const char **argv) {
int fd, ret;
cmd_t cmd;
cmd = parse_arguments(argc, argv);
fd = open(CDEV_NAME, O_RDONLY);
if(fd < 0) {
perror("cdev open");
return EXIT_FAILURE;
}
printf("Device (%s) opened\n", CDEV_NAME);
ret = do_op(fd, cmd);
if(close(fd) != 0) {
perror("cdev close");
return EXIT_FAILURE;
}
printf("Device (%s) closed\n", CDEV_NAME);
return (ret != 0)? EXIT_FAILURE : EXIT_SUCCESS;
}