I'm trying to get the original destination of UDP packet using IP_ORIGDSTADDR. On older kernel that mine, it seems to work (I'm running the currently debian testing kernel, 4.7.0-1-amd64).
#define _DEFAULT_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#define DEFAULT_ADDR "127.0.0.1"
#define DEFAULT_PORT 6666
int main(int ac, char **av)
{
int sock;
struct sockaddr_in sin;
memset(&sin, 0, sizeof sin);
sin.sin_family = AF_INET;
if (inet_aton(ac >= 2 ? av[1] : DEFAULT_ADDR, &sin.sin_addr) < 0) {
fprintf(stderr, "Invalid address\n");
goto err;
}
sin.sin_port = htons(ac >= 3 ? atoi(av[2]) : DEFAULT_PORT);
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0) {
perror("socket");
goto err;
}
if (bind(sock, (struct sockaddr *) &sin, sizeof sin) < 0) {
perror("bind");
goto close_err;
}
#define SOCK_OPT(l, n, v) do { \
int _v = v; \
socklen_t _s; \
if (setsockopt(sock, l, n, &_v, sizeof _v) < 0) { \
perror("setsockopt "# l "/" # n); \
goto close_err; \
} \
\
_s = sizeof (_v); \
if (getsockopt(sock, l, n, &_v, &_s) < 0) { \
perror("getsockopt "# l "/" # n); \
goto close_err; \
} \
\
if (_v != v) { \
fprintf(stderr, "Unexpected sockopt (expected %d, found %d)\n", v, _v); \
goto close_err; \
} \
\
printf(#l "/" #n " is set to %d\n", _v); \
\
} while (0)
SOCK_OPT(SOL_IP, IP_RECVORIGDSTADDR, 1);
SOCK_OPT(SOL_IP, IP_RECVOPTS, 1);
SOCK_OPT(SOL_IP, IP_PKTINFO, 1);
#undef SOCK_OPT
printf("Reading on %s:%d\n", inet_ntoa(sin.sin_addr), htons(sin.sin_port));
for (;;) {
ssize_t n;
char buf[1024];
char tmp[80];
struct iovec iovec[] = {
{
.iov_base = buf,
.iov_len = sizeof buf - 1,
}
};
struct msghdr msghdr;
struct cmsghdr *cmsg_ptr;
struct sockaddr_storage from = { 0 };
int port;
union cmsg_data {
struct sockaddr_in sin;
struct in_pktinfo pktinfo;
};
char msg_control[CMSG_SPACE(sizeof(union cmsg_data)) * 10] = { 0 };
int found;
memset(&msghdr, 0, sizeof msghdr);
msghdr.msg_name = &from;
msghdr.msg_namelen = sizeof from;
msghdr.msg_iov = iovec;
msghdr.msg_iovlen = sizeof iovec / sizeof iovec[0];
msghdr.msg_control = msg_control;
msghdr.msg_controllen = sizeof msg_control;
msghdr.msg_flags = MSG_EOR | MSG_TRUNC | MSG_CTRUNC | MSG_OOB | MSG_ERRQUEUE;
n = recvmsg(sock, &msghdr, MSG_OOB);
if (n < 0) {
perror("recvmsg");
continue;
}
if (buf[n - 1] == '\n')
n--;
buf[n] = 0;
switch (from.ss_family) {
default:
tmp[0] = 0;
break;
case AF_INET:
inet_ntop(AF_INET, &((struct sockaddr_in *) &from)->sin_addr, tmp, sizeof tmp);
port = htons(((struct sockaddr_in *) &from)->sin_port);
break;
case AF_INET6:
inet_ntop(AF_INET6, &((struct sockaddr_in6 *) &from)->sin6_addr, tmp, sizeof tmp);
port = htons(((struct sockaddr_in6 *) &from)->sin6_port);
break;
}
printf("%s:%d Rx %ldb: %.*s, msg_control = %zdb\n", tmp, port, n, (int) n, buf, sizeof msg_control);
found = 0;
for (cmsg_ptr = CMSG_FIRSTHDR(&msghdr); cmsg_ptr != NULL; cmsg_ptr = CMSG_NXTHDR(&msghdr, cmsg_ptr)) {
union cmsg_data *cmsg_data = (union cmsg_data *) CMSG_DATA(cmsg_ptr);
switch (cmsg_ptr->cmsg_level) {
default:
fprintf(stderr, "Unexecpted level : %d\n", cmsg_ptr->cmsg_level);
break;
case SOL_IP:
switch (cmsg_ptr->cmsg_type) {
default:
fprintf(stderr, "Unexecpted type : %d\n", cmsg_ptr->cmsg_type);
break;
case IP_ORIGDSTADDR:
printf("IP_ORIGDSTADDR: sin_addr = %s, sin_port = %d\n", inet_ntoa(cmsg_data->sin.sin_addr), htons(cmsg_data->sin.sin_port));
found++;
break;
case IP_PKTINFO:
snprintf(tmp, sizeof tmp, "%s", inet_ntoa(cmsg_data->pktinfo.ipi_spec_dst));
printf("IP_PKTINFO: ifindex = %u, spec_dst = %s, addr = %s\n", cmsg_data->pktinfo.ipi_ifindex, tmp, inet_ntoa(cmsg_data->pktinfo.ipi_addr));
break;
}
}
}
if (found != 1)
fprintf(stderr, "*** Warning: No SOL_IP / IP_ORIGDSTADDR found\n");
}
close_err:
close(sock);
err:
return 1;
}
When trying this bunch of code (eg. sending packets using netcat), I dont have any IP_ORIGDSTADDR, but only IP_PKTINFO : I need to have UDP port, only IP_ORIGDSTADDR can provide it.
Does anyone have met this strange behaviour ?
Related
Many times I have typical ETL code that looks like this
./call_some_api.py | ./extract_transform | ./load_to_some_sql
What if either of the first two scripts stop sending bytes because of some internal error that causes them to stall. I wish there was another program I could put before ./load_to_some_sql that will detect that 0 bytes has been sent in 5 minutes, and throw an exit code.
Curl has --speed-limit and --speed-time which do exactly this, but it's not generalized for other cli apps.
Is there a way to do this? Have a way to crash if the throughput hits a certain level on a pipe?
I know state machines and other orchestration tools can do this, but in general if there's a way to do it from bash it would be helpful!
If you are interested in inactivity timeout, these can do it.
One liner (mentioned in comments). Adjust $t for inactivity seconds:
perl -e'$t=300;$SIG{ALRM}=sub{die"Timeout\n"};alarm$t;while(<>){print;alarm$t}'
More robust:
#include <sys/epoll.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <signal.h>
long secs = 300;
int stdin_mode, stdout_mode;
void my_exit(int code) {
fcntl(STDIN_FILENO, F_SETFL, stdin_mode);
fcntl(STDOUT_FILENO, F_SETFL, stdout_mode);
exit(code);
}
char* progname = "";
void perror_die(char* msg) {
dprintf(STDERR_FILENO, "%s: %s: %m\n", progname, msg);
my_exit(EXIT_FAILURE);
}
int check(int ret, char *msg) {
if (ret == -1) perror_die(msg);
return ret;
}
void usage() {
dprintf(STDERR_FILENO, "Usage:\n-s <secs> inactivity timeout in seconds (default: %ld seconds)\n", secs);
exit(EXIT_SUCCESS);
}
int main(int argc, char* argv[]) {
progname = argv[0];
signal(SIGHUP, SIG_IGN);
stdin_mode = fcntl(STDIN_FILENO, F_GETFL);
stdout_mode = fcntl(STDOUT_FILENO, F_GETFL);
int opt;
while((opt = getopt(argc, argv, "s:h")) != -1)
switch(opt) {
case 's':
secs = strtol(optarg, NULL, 10);
break;
case 'h':
case '?':
usage();
};
if (optind < argc) usage();
int epfd = check(epoll_create(3), "epoll_create");
struct epoll_event ev = {
.events = EPOLLIN | EPOLLET,
.data.fd = STDIN_FILENO
};
check(epoll_ctl(epfd, EPOLL_CTL_ADD, STDIN_FILENO, &ev), "epoll_ctl");
check(fcntl(STDIN_FILENO, F_SETFL, stdin_mode | O_NONBLOCK), "fcntl stdin");
ev.events = 0;
ev.data.fd = STDOUT_FILENO;
int epout = !epoll_ctl(epfd, EPOLL_CTL_ADD, STDOUT_FILENO, &ev);
if (epout)
check(fcntl(STDOUT_FILENO, F_SETFL, stdout_mode | O_NONBLOCK), "fcntl stdout");
char buf[4096];
ssize_t b_read = 0, b_wrote = 0, bytes = 0;
secs *= 1000;
int epres;
while((epres = epoll_wait(epfd, &ev, 1, secs)) == 1) {
if (ev.events & EPOLLERR) {
errno = EPIPE;
break;
}
for(;;) {
if (b_read == 0) {
b_read = read(STDIN_FILENO, buf, sizeof(buf));
if (b_read > 0) bytes += b_read;
else if (b_read == -1) {
if (errno == EAGAIN) {
b_read = 0;
break;
} else perror_die("read");
} else my_exit(EXIT_SUCCESS);
}
if (b_read) {
int w = write(STDOUT_FILENO, buf + b_wrote, b_read - b_wrote);
if (w != -1) b_wrote += w;
else {
if (errno == EAGAIN && epout) {
ev.events = EPOLLOUT | EPOLLONESHOT;
ev.data.fd = STDOUT_FILENO;
check(epoll_ctl(epfd, EPOLL_CTL_MOD, STDOUT_FILENO, &ev), "epoll_ctl");
break;
} else perror_die("write");
}
if (b_wrote == b_read) b_read = b_wrote = 0;
}
}
}
if (epres) perror_die("event loop");
dprintf(STDERR_FILENO, "%s: Timeout reached\n", progname);
my_exit(EXIT_FAILURE);
}
If you are interested in measuring bytes/secs this can do it, on an interval: (calculation could be improved to keep last n secs bandwidth instead of an interval)
#include <sys/epoll.h>
#include <fcntl.h>
#include <sys/timerfd.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <signal.h>
ssize_t min_bw = 1000;
long secs = 5;
int stdin_mode, stdout_mode;
void my_exit(int code) {
fcntl(STDIN_FILENO, F_SETFL, stdin_mode);
fcntl(STDOUT_FILENO, F_SETFL, stdout_mode);
exit(code);
}
char* progname = "";
void perror_die(char* msg) {
dprintf(STDERR_FILENO, "%s: %s: %m\n", progname, msg);
my_exit(EXIT_FAILURE);
}
int check(int ret, char *msg) {
if (ret == -1) perror_die(msg);
return ret;
}
void usage() {
dprintf(STDERR_FILENO, "Usage:\n-b <bytes> minimum bytes per (default: %ld bytes)\n-s <secs> seconds (default: %ld seconds)\n", min_bw, secs);
exit(EXIT_SUCCESS);
}
int main(int argc, char* argv[]) {
progname = argv[0];
signal(SIGHUP, SIG_IGN);
stdin_mode = fcntl(STDIN_FILENO, F_GETFL);
stdout_mode = fcntl(STDOUT_FILENO, F_GETFL);
int opt;
while((opt = getopt(argc, argv, "b:s:h")) != -1)
switch(opt) {
case 'b':
min_bw = strtol(optarg, NULL, 10);
break;
case 's':
secs = strtol(optarg, NULL, 10);
break;
case 'h':
case '?':
usage();
};
if (optind < argc) usage();
int epfd = check(epoll_create(3), "epoll_create");
int tmfd = check(timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK), "timerfd_create");
struct itimerspec when = {{secs, 0}, {secs, 0}};
check(timerfd_settime(tmfd, 0, &when, NULL), "timerfd_settime");
struct epoll_event ev = {
.events = EPOLLIN | EPOLLET,
.data.fd = tmfd
};
check(epoll_ctl(epfd, EPOLL_CTL_ADD, tmfd, &ev), "epoll_ctl");
ev.data.fd = STDIN_FILENO;
check(epoll_ctl(epfd, EPOLL_CTL_ADD, STDIN_FILENO, &ev), "epoll_ctl");
check(fcntl(STDIN_FILENO, F_SETFL, stdin_mode | O_NONBLOCK), "fcntl stdin");
ev.events = 0;
ev.data.fd = STDOUT_FILENO;
int epout = !epoll_ctl(epfd, EPOLL_CTL_ADD, STDOUT_FILENO, &ev);
if (epout)
check(fcntl(STDOUT_FILENO, F_SETFL, stdout_mode | O_NONBLOCK), "fcntl stdout");
char tmbuf[64];
char buf[4096];
ssize_t b_read = 0, b_wrote = 0, bytes = 0;
int epres;
while((epres = epoll_wait(epfd, &ev, 1, -1)) == 1) {
if (ev.events & EPOLLERR) {
errno = EPIPE;
break;
}
if (ev.data.fd == tmfd) {
if (bytes < min_bw) {
dprintf(STDERR_FILENO, "Too slow\n");
my_exit(EXIT_FAILURE);
}
check(read(tmfd, tmbuf, sizeof(tmbuf)), "read timerfd");
bytes = 0;
continue;
}
for(;;) {
if (b_read == 0) {
b_read = read(STDIN_FILENO, buf, sizeof(buf));
if (b_read > 0) bytes += b_read;
else if (b_read == -1) {
if (errno == EAGAIN) {
b_read = 0;
break;
} else perror_die("read");
} else my_exit(EXIT_SUCCESS);
}
if (b_read) {
int w = write(STDOUT_FILENO, buf + b_wrote, b_read - b_wrote);
if (w != -1) b_wrote += w;
else {
if (errno == EAGAIN && epout) {
ev.events = EPOLLOUT | EPOLLONESHOT;
ev.data.fd = STDOUT_FILENO;
check(epoll_ctl(epfd, EPOLL_CTL_MOD, STDOUT_FILENO, &ev), "epoll_ctl");
break;
} else perror_die("write");
}
if (b_wrote == b_read) b_read = b_wrote = 0;
}
}
}
perror_die("event loop");
}
Both programs are for linux only.
Provided you have a newish version of Bash, the read builtin can optionally enforce a timeout [docs], which means that it's actually not hard to write a bit of pure Bash code that does exactly what you describe:
... upstream command ... \
| {
while true ; do
read -r -N 1 -t 300 char
result=$?
if (( result > 128 )) ; then
echo 'Read timed out.' >&2
exit 1
elif (( result > 0 )) ; then
exit 0
elif [[ "$char" == '' ]] ; then
printf '\0'
else
printf %s "$char"
fi
done
} \
| ... downstream command ...
(Note: the above reads one character at a time for simplicity's sake; if you expect a high volume of data to pass through this, then you may need to adjust it for better performance.)
But even though the above does exactly what you describe, I'm not sure whether it will really accomplish your goal, because the upstream command won't actually die until it tries to write something and gets SIGPIPE. So even after the above prints Read timed out, Bash will just keep hanging indefinitely, probably until the user hits Ctrl-C. (The same goes for niry's answer, of course.) That's a bit harder to fix; I guess when your command detects a timeout, it will need to find the process-IDs of the upstream commands, and use kill to proactively send them SIGPIPE (or some other signal of your choosing)?
I'm recording RTSP stream from camera into mp4 files in Windows machine and I want to save files in separate folder by day using strftime option (name format like D:/Video/YYYY-MM-DD/). I really want to know that does ffmpeg have ability to create folder by itself or do I have to create it by external programs?
For example, I want to use ffmpeg command like below:
ffmpeg -rtsp_transport tcp -i <rtsp_url> \
-f segment -strftime 1 -segment_time 01:00:00 -segment_atclocktime 1 \
-segment_clocktime_offset 30 -segment_format mp4 \
-an -vcodec copy -reset_timestamps 1 \
D:/Video/%Y-%m-%d/record_%H_%M_%S.mp4
Here is a patch for ffmpeg-3.3.6
diff --git a/libavformat/segment.c b/libavformat/segment.c
index 8ec3653..2d90ef2 100644
--- a/libavformat/segment.c
+++ b/libavformat/segment.c
## -28,6 +28,7 ##
#include <float.h>
#include <time.h>
+#include <unistd.h>
#include "avformat.h"
#include "avio_internal.h"
## -226,6 +227,49 ## static int set_segment_filename(AVFormatContext *s)
return 0;
}
+/* Note: This modifies *dir */
+static int create_dir(char *dir, mode_t mode) {
+ int ret = 0;
+ unsigned int i, dlen;
+ /* Shortcut, there are no deep directories */
+ if (strchr(dir, '/') == NULL)
+ return mkdir(dir, mode);
+ dlen = strlen(dir);
+ /* Skip first char (it can be /) */
+ for (i = 1; i < dlen; i++) {
+ if (dir[i] != '/')
+ continue;
+ dir[i] = '\0';
+ ret = mkdir(dir, mode);
+ dir[i] = '/';
+ if (ret < 0 && errno != EEXIST)
+ goto OUT;
+ }
+ ret = mkdir(dir, mode);
+OUT:
+ return ret;
+}
+
+static int segment_create_directory(AVFormatContext *oc, char *filename) {
+ char *dir, *fname;
+ /* Do nothing when the filename is URL */
+ if (strstr(filename, "://") != NULL)
+ return 0;
+ fname = av_strdup(filename);
+ if (!fname)
+ return AVERROR(ENOMEM);
+ dir = (char *)av_dirname(fname);
+ if (access(dir, W_OK) != F_OK)
+ av_log(oc, AV_LOG_INFO, "Create directory %s\n", dir);
+ if (create_dir(dir, 0777) == -1 && errno != EEXIST) {
+ av_log(oc, AV_LOG_ERROR, "Could not create directory %s\n", dir);
+ av_free(fname);
+ return AVERROR(errno);
+ }
+ av_free(fname);
+ return 0;
+}
+
static int segment_start(AVFormatContext *s, int write_header)
{
SegmentContext *seg = s->priv_data;
## -247,6 +291,9 ## static int segment_start(AVFormatContext *s, int write_header)
if ((err = set_segment_filename(s)) < 0)
return err;
+ if ((err = segment_create_directory(s, oc->filename)) < 0)
+ return err;
+
if ((err = s->io_open(s, &oc->pb, oc->filename, AVIO_FLAG_WRITE, NULL)) < 0) {
av_log(s, AV_LOG_ERROR, "Failed to open segment '%s'\n", oc->filename);
return err;
## -277,6 +324,10 ## static int segment_list_open(AVFormatContext *s)
int ret;
snprintf(seg->temp_list_filename, sizeof(seg->temp_list_filename), seg->use_rename ? "%s.tmp" : "%s", seg->list);
+
+ if ((ret = segment_create_directory(s, seg->temp_list_filename)) < 0)
+ return ret;
+
ret = s->io_open(s, &seg->list_pb, seg->temp_list_filename, AVIO_FLAG_WRITE, NULL);
if (ret < 0) {
av_log(s, AV_LOG_ERROR, "Failed to open segment list '%s'\n", seg->list);
## -746,6 +797,9 ## static int seg_init(AVFormatContext *s)
oc = seg->avf;
if (seg->write_header_trailer) {
+ if ((ret = segment_create_directory(s, seg->header_filename ? seg->header_filename : oc->filename)) < 0)
+ return ret;
+
if ((ret = s->io_open(s, &oc->pb,
seg->header_filename ? seg->header_filename : oc->filename,
AVIO_FLAG_WRITE, NULL)) < 0) {
It can be helpful to creating a patch for last version to support making directories for segmentation.
I would like to know the CR0-CR4 register values on x86. Can I write inline assembly to read it out? Are there any other methods? (e.g., does OS keep any file structures to record these values)
The Linux kernel has some function to read and write Control Registers, they are the read_crX and write_crX functions for the standard CR and xgetbv,xsetbv for the extended CR.
User mode applications need a LKM to indirectly use these functions.
In theory you just need to create a LKM with one or more devices and handle IO requests by reading or writing from CR. In practice you usually have more than one CPU, so you need to handle MP.
I used the kernel module for CPUID as a template and create this LKM.
CODE IS WITHOUT ANY WARRANTY, TESTED ON DEBIAN 8 ON 64 bit VM ONLY
#include <linux/module.h> /* Needed by all modules */
#include <linux/kernel.h> /* Needed for KERN_INFO */
#include <linux/fs.h> /* Needed for KERN_INFO */
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/fcntl.h>
#include <linux/init.h>
#include <linux/poll.h>
#include <linux/smp.h>
#include <linux/major.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cpu.h>
#include <linux/notifier.h>
#include <linux/uaccess.h>
#include <linux/gfp.h>
#include <asm/processor.h>
#include <asm/msr.h>
#include <asm/xcr.h>
#define MAKE_MINOR(cpu, reg) (cpu<<8 | reg)
#define GET_MINOR_REG(minor) (minor & 0xff)
#define GET_MINOR_CPU(minor) (minor >> 8)
#define XCR_MINOR_BASE 0x80
static int major_n = 0;
static struct class *ctrlreg_class;
struct ctrlreg_info
{
unsigned int reg;
unsigned long value;
unsigned int error;
};
static void ctrlreg_smp_do_read(void* p)
{
struct ctrlreg_info* info = p;
info->error = 0;
printk(KERN_INFO "ctrlreg: do read of reg%u\n", info->reg);
switch (info->reg)
{
case 0: info->value = read_cr0(); break;
case 2: info->value = read_cr2(); break;
case 3: info->value = read_cr3(); break;
case 4: info->value = read_cr4(); break;
#ifdef CONFIG_X86_64
case 8: info->value = read_cr8(); break;
#endif
case XCR_MINOR_BASE: info->value = xgetbv(0); break;
default:
info->error = -EINVAL;
}
}
static void ctrlreg_smp_do_write(void* p)
{
struct ctrlreg_info* info = p;
info->error = 0;
switch (info->reg)
{
case 0: write_cr0(info->value); break;
case 2: write_cr2(info->value); break;
case 3: write_cr3(info->value); break;
case 4: write_cr4(info->value); break;
#ifdef CONFIG_X86_64
case 8: read_cr8(); break;
#endif
case XCR_MINOR_BASE: xgetbv(0); break;
default:
info->error = -EINVAL;
}
}
static ssize_t ctrlreg_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
unsigned int minor = iminor(file_inode(file));
unsigned int cpu = GET_MINOR_CPU(minor);
unsigned int reg = GET_MINOR_REG(minor);
struct ctrlreg_info info = {.reg = reg};
int err;
printk(KERN_INFO "ctrlreg: read for cpu%u reg%u\n", cpu, reg);
printk(KERN_INFO "ctrlreg: read of %zu bytes\n", count);
if (count < sizeof(unsigned long))
return -EINVAL;
printk(KERN_INFO "ctrlreg: scheduling read\n");
err = smp_call_function_single(cpu, ctrlreg_smp_do_read, &info, 1);
if (IS_ERR_VALUE(err))
return err;
printk(KERN_INFO "ctrlreg: read success: %x\n", info.error);
if (IS_ERR_VALUE(info.error))
return err;
err = copy_to_user(buf, &info.value, sizeof(unsigned long));
printk(KERN_INFO "ctrlreg: read copy result: %x ( %lu )\n", err, sizeof(unsigned long));
if (IS_ERR_VALUE(err))
return err;
printk(KERN_INFO "ctrlreg: read done\n");
return sizeof(unsigned long);
}
static ssize_t ctrlreg_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
unsigned int minor = iminor(file_inode(file));
unsigned int cpu = GET_MINOR_CPU(minor);
unsigned int reg = GET_MINOR_REG(minor);
struct ctrlreg_info info = {.reg = reg};
int err;
printk(KERN_INFO "ctrlreg: write for cpu%u reg%u\n", cpu, reg);
printk(KERN_INFO "ctrlreg: write of %zu bytes\n", count);
if (count < sizeof(unsigned long))
return -EINVAL;
printk(KERN_INFO "ctrlreg: scheduling write\n");
err = copy_from_user((void*)buf, &info.value, sizeof(unsigned long));
printk(KERN_INFO "ctrlreg: write copy data: %x ( %lu )\n", err, sizeof(unsigned long));
if (IS_ERR_VALUE(err))
return err;
err = smp_call_function_single(cpu, ctrlreg_smp_do_write, &info, 1);
if (IS_ERR_VALUE(err))
return err;
printk(KERN_INFO "ctrlreg: write success: %x\n", info.error);
if (IS_ERR_VALUE(info.error))
return err;
printk(KERN_INFO "ctrlreg: write done\n");
return sizeof(unsigned long);
}
static void ctrlreg_can_open(void *p)
{
unsigned int* reg = p;
unsigned int reg_num = *reg;
unsigned int ebx, edx, eax, ecx;
unsigned int support_xgetbv, support_ia32e;
*reg = 0; //Success
printk(KERN_INFO "ctrlreg: can open reg %u\n", reg_num);
if (reg_num <= 4 && reg_num != 1)
return;
#ifdef CONFIG_X86_64
if (reg_num == 8)
return;
#endif
cpuid_count(0x0d, 1, &eax, &ebx, &ecx, &edx);
support_xgetbv = cpuid_ecx(1) & 0x04000000;
support_ia32e = cpuid_edx(0x80000001) & 0x20000000;
printk(KERN_INFO "ctrlreg: xgetbv = %d\n", support_xgetbv);
printk(KERN_INFO "ctrlreg: ia32e = %d\n", support_ia32e);
if (support_xgetbv && support_ia32e)
return;
printk(KERN_INFO "ctrlreg: open denied");
*reg = -EIO;
}
static int ctrlreg_open(struct inode *inode, struct file *file)
{
unsigned int cpu;
unsigned int reg;
unsigned int minor;
int err;
minor = iminor(file_inode(file));
cpu = GET_MINOR_CPU(minor);
reg = GET_MINOR_REG(minor);
printk(KERN_INFO "ctrlreg: open device for cpu%u reg%u\n", cpu, reg);
if (cpu >= nr_cpu_ids || !cpu_online(cpu))
return -ENXIO; /* No such CPU */
err = smp_call_function_single(cpu, ctrlreg_can_open, ®, 1);
if (IS_ERR_VALUE(err))
return err;
return reg;
}
static const struct file_operations ctrlreg_fops =
{
.owner = THIS_MODULE,
.read = ctrlreg_read,
.write = ctrlreg_write,
.open = ctrlreg_open
};
static int ctrlreg_device_create(int cpu)
{
struct device *dev = NULL;
int i;
printk(KERN_INFO "ctrlreg: device create for cpu %d\n", cpu);
//CR0, 2-4, 8
for (i = 0; i <= 8; i++)
{
if ((i>4 && i<8) || i == 1)
continue; //Skip non existent regs
printk(KERN_INFO "ctrlreg: device cpu%dcr%d\n", cpu, i);
dev = device_create(ctrlreg_class, NULL, MKDEV(major_n, MAKE_MINOR(cpu, i)), NULL, "cpu%dcr%d", cpu, i);
if (IS_ERR(dev))
return PTR_ERR(dev);
}
//XCR0
for (i = 0; i <= 0; i++)
{
printk(KERN_INFO "ctrlreg: device cpu%dxcr%d\n", cpu, i);
dev = device_create(ctrlreg_class, NULL, MKDEV(major_n, MAKE_MINOR(cpu, (XCR_MINOR_BASE+i))), NULL, "cpu%dxcr%d", cpu, i);
if (IS_ERR(dev))
return PTR_ERR(dev);
}
return 0;
}
static void ctrlreg_device_destroy(int cpu)
{
int i;
//CR0, 2-4, 8
for (i = 0; i <= 8; i++)
{
if ((i>4 && i<8) || i == 1)
continue; //Skip non existent regs
device_destroy(ctrlreg_class, MKDEV(major_n, MAKE_MINOR(cpu, i)));
}
//XCR0
for (i = 0; i <= 0; i++)
device_destroy(ctrlreg_class, MKDEV(major_n, MAKE_MINOR(cpu, (XCR_MINOR_BASE+i))));
}
static int ctrlreg_class_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu)
{
unsigned int cpu = (unsigned long)hcpu;
int err = 0;
switch (action)
{
case CPU_UP_PREPARE:
err = ctrlreg_device_create(cpu);
break;
case CPU_UP_CANCELED:
case CPU_UP_CANCELED_FROZEN:
case CPU_DEAD:
ctrlreg_device_destroy(cpu);
break;
}
return notifier_from_errno(err);
}
static struct notifier_block __refdata ctrlreg_class_cpu_notifier =
{
.notifier_call = ctrlreg_class_cpu_callback,
};
static char* ctrlreg_devnode(struct device *dev, umode_t *mode)
{
unsigned int minor = MINOR(dev->devt), cpu = GET_MINOR_CPU(minor), reg = GET_MINOR_REG(minor);
if (reg < XCR_MINOR_BASE)
return kasprintf(GFP_KERNEL, "crs/cpu%u/cr%u", cpu, reg);
else
return kasprintf(GFP_KERNEL, "crs/cpu%u/xcr%u", cpu, reg-XCR_MINOR_BASE);
}
int __init ctrlreg_init(void)
{
int err = 0, i = 0;
printk(KERN_INFO "ctrlreg: init\n");
if ((major_n = __register_chrdev(0, 0, NR_CPUS, "crs", &ctrlreg_fops)) < 0)
return major_n;
printk(KERN_INFO "ctrlreg: major number is %u\n", major_n);
ctrlreg_class = class_create(THIS_MODULE, "ctrlreg\n");
if (IS_ERR(ctrlreg_class))
{
err = PTR_ERR(ctrlreg_class);
goto out_chrdev;
}
printk(KERN_INFO "ctrlreg: class created\n");
ctrlreg_class->devnode = ctrlreg_devnode;
cpu_notifier_register_begin();
for_each_online_cpu(i)
{
err = ctrlreg_device_create(i);
if (IS_ERR_VALUE(err))
goto out_class;
}
__register_hotcpu_notifier(&ctrlreg_class_cpu_notifier);
cpu_notifier_register_done();
printk(KERN_INFO "ctrlreg: init success\n");
err = 0;
goto out;
out_class:
i = 0;
for_each_online_cpu(i)
{
ctrlreg_device_destroy(i);
}
cpu_notifier_register_done();
class_destroy(ctrlreg_class);
out_chrdev:
__unregister_chrdev(CPUID_MAJOR, 0, NR_CPUS, "ctrlreg");
out:
return err;
}
static void __exit ctrlreg_exit(void)
{
int cpu = 0;
cpu_notifier_register_begin();
for_each_online_cpu(cpu)
ctrlreg_device_destroy(cpu);
class_destroy(ctrlreg_class);
__unregister_chrdev(CPUID_MAJOR, 0, NR_CPUS, "ctrlreg");
__unregister_hotcpu_notifier(&ctrlreg_class_cpu_notifier);
cpu_notifier_register_done();
}
module_init(ctrlreg_init);
module_exit(ctrlreg_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Kee Nemesis 241");
MODULE_DESCRIPTION("Read and write Control Registers");
This module create the following dev nodes:
/dev/crs/cpu0/cr0
/dev/crs/cpu0/cr2
/dev/crs/cpu0/cr3
/dev/crs/cpu0/cr4
/dev/crs/cpu0/cr8
/dev/crs/cpu0/xcr0
/dev/crs/cpu1/cr0
/dev/crs/cpu1/cr2
/dev/crs/cpu1/cr3
/dev/crs/cpu1/cr4
/dev/crs/cpu1/cr8
/dev/crs/cpu1/xcr0
...
You can read/write these dev nodes. The minimum read/write length is 4 bytes on 32 bit system and 8 bytes on 64 bit ones (Linux do some buffering anyway).
To compile this LKM, save the code above as ctrlreg.c and create this Makefile
obj-m += ctrlreg.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
then use make to get ctrlreg.ko.
To load the module use sudo insmod ctrlreg.ko, to remove it sudo rmmod ctrlreg.
I have also written a small user mode utility to read CR:
CODE IS WITHOUT ANY WARRANTY, TESTED ON DEBIAN 8 ON 64 bit VM ONLY
#include <stdio.h>
#include <stdlib.h>
#define MAX_PATH 256
int main(int argc, char* argv[])
{
unsigned long cpu, reg;
FILE* fin;
char device[MAX_PATH];
unsigned long data;
if (argc < 3 || argc > 4)
return fprintf(stderr, "Usage:\n\t\t cr cpu reg [value]\n"), 1;
if (sscanf(argv[1], "cpu%u", &cpu) != 1)
return fprintf(stderr, "Invalid value '%s' for cpu\n", argv[1]), 2;
if (sscanf(argv[2], "cr%u", ®) != 1 && sscanf(argv[2], "xcr%u", ®) != 1)
return fprintf(stderr, "Invalid value '%s' for reg\n", argv[2]), 3;
if (argc == 4 && sscanf(argv[3], "%lu", &data) != 1)
return fprintf(stderr, "Invalid numeric value '%s'\n", argv[3]), 6;
snprintf(device, MAX_PATH, "/dev/crs/cpu%u/%s", cpu, argv[2]);
fin = fopen(device, argc == 4 ? "wb" : "rb");
if (!fin)
return fprintf(stderr, "Cannot open device %s\n", device), 4;
if (argc == 4)
{
if (fwrite(&data, sizeof(data), 1, fin) != 1)
return fprintf(stderr, "Cannot write device %s (%d)\n", device, ferror(fin)), 5;
}
else
{
if (fread(&data, sizeof(data), 1, fin) != 1)
return fprintf(stderr, "Cannot read device %s (%d)\n", device, ferror(fin)), 7;
printf("%016x\n", data);
}
fclose(fin);
return 0;
}
Save the code as cr.c and compile it.
To read cr0 of the second CPU you can use:
cr cpu1 cr0
To write the value 0 (be careful) into it
cr cpu1 cr0 0
I'm trying to give a better error report (possible bug) for this case (about judySArray give incorrect result, but I don't know which key that give incorrect result).
The code here from this folder, note on this blog. Dependencies: judySArray.h and cedar.h
// judy.cpp
#include "deps/judySArray.h"
#include <string>
#include <iostream>
#include <cstdlib>
#include <cstring>
using namespace std;
typedef judySArray<double> MSD;
const int MAX_DATA = 12000000;
const char i2ch[] = {'0','1','2','3','4','5','6','7','8','9','a','B','c','D','e','F'};
int get_first_digit(double d) {
while(d > 10) d /= 10;
return d;
}
string to_rhex(int v) {
char hex[32];
int start = 0;
while(v>0) {
hex[start] = i2ch[v%16];
v /= 16;
++start;
}
hex[start] = 0;
return hex;
}
void add_or_inc(MSD &m, const string& key,double set, double inc, int& ctr) {
const char* cstr = key.c_str();
double it = m.find(cstr);
if(!it) {
m.insert(cstr,set);
return;
}
m.insert(cstr,it+inc);
++ctr;
}
int main() {
MSD m(64);
int dup1 = 0, dup2 = 0, dup3 = 0;
for(int z=MAX_DATA;z>0;--z) {
int val2 = MAX_DATA-z;
int val3 = MAX_DATA*2-z;
string key1 = to_string(z);
string key2 = to_string(val2);
string key3 = to_rhex(val3);
add_or_inc(m,key1,z,val2,dup1);
add_or_inc(m,key2,val2,val3,dup2);
add_or_inc(m,key3,val3,z,dup3);
}
cout << dup1 << ' ' << dup2 << ' ' << dup3 << endl;
int total = 0, verify = 0, count = 0;
for(auto &it = m.begin();m.success(); m.next()) {
total += get_first_digit(it.value);
verify += strlen((const char *) it.key);
count += 1;
}
cout << total << ' ' << verify << ' ' << count << endl;
}
other implementation (map, unordered_map, hat-trie and cedar) give correct result:
6009354 6009348 611297
36186112 159701682 23370001
but judy didn't:
6009354 6009348 611297
36186112 159701681 23370000
The problem is, which key that have incorrect result?
I've tried to build a code that insert those keys on another data structure (that is cedar), but that incorrect keys still not detected:
// judy.cpp
#include "deps/judySArray.h"
#include <string>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <vector>
using namespace std;
typedef judySArray<double> MSD;
const int MAX_DATA = 12000000;
const char i2ch[] = {'0','1','2','3','4','5','6','7','8','9','a','B','c','D','e','F'};
int get_first_digit(double d) {
while(d > 10) d /= 10;
return d;
}
string to_rhex(int v) {
char hex[32];
int start = 0;
while(v>0) {
hex[start] = i2ch[v%16];
v /= 16;
++start;
}
hex[start] = 0;
return hex;
}
void add_or_inc(MSD &m, const string& key,double set, double inc, int& ctr) {
const char* cstr = key.c_str();
double it = m.find(cstr);
if(!it) {
m.insert(cstr,set);
return;
}
m.insert(cstr,it+inc);
++ctr;
}
#include "deps/cedar.h"
class MSD2 {
public:
vector<double> data;
typedef cedar::da<int> CI;
CI da;
bool exists(const string& key,double &old) {
int idx = -1;
bool found = da.exactMatchExists(key.c_str(),key.size(),&idx);
if(found) old = data[idx];
return found;
}
void insert(const string& key,double val) {
da.update(key.c_str(),key.size(),data.size());
data.push_back(val);
}
void update(const string& key,double val) {
int idx = -1;
bool found = da.exactMatchExists(key.c_str(),key.size(),&idx);
if(found) {
data[idx] = val;
return;
}
insert(key,val);
}
};
void add_or_inc(MSD2 &m, const string& key,double set, double inc, int& ctr) {
double old;
if(!m.exists(key,old)) {
m.insert(key,set);
return;
}
m.update(key,old+inc);
++ctr;
}
int main() {
MSD m(64);
MSD2 m2;
int dup1 = 0, dup2 = 0, dup3 = 0;
int vup1 = 0, vup2 = 0, vup3 = 0;
for(int z=MAX_DATA;z>0;--z) {
int val2 = MAX_DATA-z;
int val3 = MAX_DATA*2-z;
string key1 = to_string(z);
string key2 = to_string(val2);
string key3 = to_rhex(val3);
add_or_inc(m,key1,z,val2,dup1);
add_or_inc(m,key2,val2,val3,dup2);
add_or_inc(m,key3,val3,z,dup3);
add_or_inc(m2,key1,z,val2,vup1);
add_or_inc(m2,key2,val2,val3,vup2);
add_or_inc(m2,key3,val3,z,vup3);
}
cout << dup1 << ' ' << dup2 << ' ' << dup3 << endl;
cout << vup1 << ' ' << vup2 << ' ' << vup3 << endl;
int total = 0, verify = 0, count = 0;
int xotal = 0, xerify = 0, xount = 0;
union { int i; int x; } b;
size_t from = 0, p = 0;
char key[256] = {0};
for (b.i = m2.da.begin(from, p); b.i != MSD2::CI::CEDAR_NO_PATH; b.i = m2.da.next(from, p)) {
double it2 = m2.data[b.x]; // <-- find cedar's
xotal += get_first_digit(it2);
m2.da.suffix(key,p,from);
xerify += strlen(key);
xount += 1;
double it = m.find(key); // <-- find judy's
if(it != it2) { // if value doesn't match, print:
cout << "mismatch value for " << key << " : " << it2 << " vs " << it << endl;
}
}
for(auto &it = m.begin();m.success(); m.next()) {
total += get_first_digit(it.value);
verify += strlen((const char *) it.key);
count += 1;
}
cout << total << ' ' << verify << ' ' << count << endl;
cout << xotal << ' ' << xerify << ' ' << xount << endl;
}
compile with: clang++ -std=c++11 judy-findbug.cpp (or g++ -std=c++11)
the output would be:
6009354 6009348 611297
6009354 6009348 611297
36186112 159701681 23370000 <-- judy's
36186112 159701682 23370001 <-- cedar's
cedar has one more value than judy's (that is correct), but it didn't detected by the code above..
How to find that incorrect key(s)?
The bug on the code is someone (me) uncomment the assert(value != 0).
The bug was Karl's Judy implementation should not store null values (0 value).
Solution: use Doug Baskins' Judy implementation.
WARNING: While resolving call to function 'help' arguments were dropped!
If I compile using gcc -O3 codice -o out/codice I get this pesky warning. It finishes compiling, so I am just wondering why this is happening.
I'm on a mac on lion with xcode 4.3 installed. The compiler it is using is i686-apple-darwin11-llvm-gcc-4.2
Note that if I switch the -O3 to -O2/-O1/-O then it continues to give the same warning
The code is as follows....
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <math.h>
void help();
char *codice_encrypt(char *in);
char *codice_decrypt(char *in);
void sigint_handler(int signal) {
exit(2);
}
int main (int argc, char **argv) {
if (argc != 3) {
help();
}
signal(SIGINT, sigint_handler);
char *ret, *status;
int tmpret;
if (strcmp(argv[1], "e") == 0 || strcmp(argv[1], "encrypt") == 0) {
status = "Encrypting...\n";
tmpret = write(STDOUT_FILENO, status, strlen((const char *)status));
ret = codice_encrypt(argv[2]);
} else if (strcmp(argv[1], "d") == 0 || strcmp(argv[1], "decrypt") == 0) {
status = "Decrypting...\n";
tmpret = write(STDOUT_FILENO, status, strlen((const char *)status));
ret = codice_decrypt(argv[2]);
} else {
status = "Could not understand command line arguments O.o\n";
tmpret = write(STDOUT_FILENO, status, strlen((const char *)status));
help(STDOUT_FILENO);
}
status = "Success!\nRetval:\n\n";
tmpret = write(STDOUT_FILENO, status, strlen((const char *)status));
tmpret = write(STDOUT_FILENO, ret, strlen((const char *)ret));
tmpret = write(STDOUT_FILENO, "\n\n", 2);
return 0;
}
void help() {
int tmpret;
const char *logo = "\n\n\t\tooooooo__oo______________________\n"
"\t\t_____oo______oo_oo_oo__oo_oo_oo__\n"
"\t\t____oo___oo__ooo_oo__o_ooo_oo__o_\n"
"\t\t___o_____oo__oo__oo__o_oo__oo__o_\n"
"\t\t_oo______oo__oo__oo__o_oo__oo__o_\n"
"\t\tooooooo_oooo_oo______o_oo______o_\n"
"\t\t_________________________________\n\n\n";
tmpret = write(STDOUT_FILENO, logo, strlen(logo));
const char *help = "Help\n"
"Is\n"
"Still\n"
"Being\n"
"Written :)\n\n";
tmpret = write(STDOUT_FILENO, help, strlen(help));
exit(1);
}
char *codice_encrypt(char *in) {
unsigned int i;
char ya = 0x55;
write(STDOUT_FILENO, "0x", 2);
char itret[50];
sprintf(itret,"%#hhx",ya);
write(STDOUT_FILENO, itret, strlen(itret));
write(STDOUT_FILENO, "\n", 1);
ya = ya & 0x0F;
ya = ya >> 1;
sprintf(itret,"%#hhx",ya);
write(STDOUT_FILENO, itret, strlen(itret));
write(STDOUT_FILENO, "\n", 1);
ya = 0x55;
ya = ya & 0xF0;
ya = ya << 1;
sprintf(itret,"%#hhx",ya);
write(STDOUT_FILENO, itret, strlen(itret));
write(STDOUT_FILENO, "\n", 1);
return in;
}
char *codice_decrypt(char *in) {
return in;
}
Thanks!
Here help() has variable number of arguments (I assume we're talking about C code). However, no arguments were passed and no arguments were read. One of optimization passes realized this and issued such a warning. Defining help() as help(void) will fix the problem.