From a4f29d82887788c3ba573aa19415b6b0d2c8e968 Mon Sep 17 00:00:00 2001 From: FreeArtMan Date: Sat, 22 Aug 2020 23:31:30 +0100 Subject: Mqueue IPC notes --- md/writeup.md | 2 +- md/writeup/mqueue_ipc_example.md | 740 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 741 insertions(+), 1 deletion(-) create mode 100644 md/writeup/mqueue_ipc_example.md (limited to 'md') diff --git a/md/writeup.md b/md/writeup.md index 94ee3ff..e1d5655 100644 --- a/md/writeup.md +++ b/md/writeup.md @@ -28,7 +28,7 @@ title: Writeup page [Compile static python](writeup/compile_python.md) [Linux hello world in Swift](writeup/linux_hello_world_in_swift.md) [Running disk images in QEMU](writeup/running_disk_images_in_qemu.md) - +[Mqueue IPC example](writeup/mqueue_ipc_example.md) ## Projects diff --git a/md/writeup/mqueue_ipc_example.md b/md/writeup/mqueue_ipc_example.md new file mode 100644 index 0000000..b98b40e --- /dev/null +++ b/md/writeup/mqueue_ipc_example.md @@ -0,0 +1,740 @@ +title:Mqueue IPC example in shell script +keywords:bash,linux,mqueue,ipc + +# Mqueue IPC example in shell script + +## Intro + +Idea of messages comes from beginning of computer history. Mainly used for inter-process +communication. Fascinating fact is no I havent seen someone using them on linux. +Using mqueue is probably most easiest IPC interface possible. Cool feature of it it have +path where you can access it. And as its act as queue then you can just open/read message +with minimal amount of code. Disadvantage is you don't know who sent you message. + +Advantages + * simple API + * no need to verify message size, comes as single packet + * persistent, if one program crashed, message stays in queue + +Disadvantages: + * cant be sure to who received message + * bidirectional communication could be harder to implement + +One of bash hacks that can be archived is inter process communication between +two bash scripts. Example will be provided! + +## Linux mqueue API + +Most system have them compiled in kernel you can check if you system supports them +```bash +$ cat /proc/filesystems | grep mq +nodev mqueue +``` + +and if its mounted with + +```bash +$ mount | grep mque +mqueue on /dev/mqueue type mqueue (rw,nosuid,nodev,noexec,relatime) +``` + +API for using mqueue is similar all UNIX interface. To check status of mqueue +use cat command + +```bash +$ cat /dev/mqueue/stack-0 +QSIZE:0 NOTIFY:0 SIGNO:0 NOTIFY_PID:0 +``` + +Further is mentioned API calls and API usage template. + + +### Library interfaces and system calls + + + Library interface System call + mq_close(3) close(2) + mq_getattr(3) mq_getsetattr(2) + mq_notify(3) mq_notify(2) + mq_open(3) mq_open(2) + mq_receive(3) mq_timedreceive(2) + mq_send(3) mq_timedsend(2) + mq_setattr(3) mq_getsetattr(2) + mq_timedreceive(3) mq_timedreceive(2) + mq_timedsend(3) mq_timedsend(2) + mq_unlink(3) mq_unlink(2) + +### Create mqueue + +Mqueue are located in mount point directory. Check with mount command where its located. +Majority system have it at /dev/mqueue . +Mqueue names passed to API should start with "/" no mount point path name needed in front. + +Create mqueue by opening it + +```c +mq_open("/name",O_RDWR | O_CREAT, 0666, NULL); +``` + +### Send mqueue message + +If mqueue already created then it will be opened, otherwise created. + + +```c +mq_open("/name",O_RDWR | O_CREAT, 0666, NULL); +mq_send(fd, "packet", strlen("packet"), set_mesg_priority); + +``` + +### Receive mqueue message + +If mqueue already created then it will be opened, otherwise created + +```c +mq_open("/name",O_RDWR | O_CREAT, 0666, NULL); +mq_receive(mq_fd, ptr_to_str, ptr_size, &get_priority); +``` + +### Remove mqueue + +Unlink file, path will disappear after all process will release file descriptors, +after unlink. + +```c +mq_open("/name",O_RDWR | O_CREAT, 0666, NULL); +mq_unlink("/name"); +``` + +## Example bash IPC + +Further is source of utilities for using mqueues. Lets create example where +one message queue created from one script, and script loops over and check +received messages from queue, and executes received commands. + +### Server script that check mqueue for commands + +loop reads queue with "./mqueue_pop -m QUEUE_NAME". If its successful +then it receives non-empty string, if not then string is empty. +Create queue if its not there with "./mqueue_create -m $QUEUE_NAME". +And after receiving stop command exits loops and unlink queue. + +3 commands supported to be sent stop/ls/aloha + + + +_daemon.sh_ +```bash +#!/bin/sh +QUEUE_NAME=/music-player-daemon +POP_CMD() +{ + ./mqueue_pop -m $QUEUE_NAME +} +CREATE_QUEUE() +{ + ./mqueue_create -m $QUEUE_NAME +} +REMOVE_QUEUE() +{ + ./mqueue_remove -m $QUEUE_NAME +} +if [ ! -f "/dev/mqueue$QUEUE_NAME" ]; then + CREATE_QUEUE +fi +state="1" +while [ $state -ne 0 ]; do + get_cmd=$(POP_CMD) + case "$get_cmd" in + stop) + state="0" + ;; + ls) + ls /dev + ;; + aloha) + echo "aloha" + ;; + esac + sleep 1 +done +if [ -f "/dev/mqueue$QUEUE_NAME" ]; then + REMOVE_QUEUE +fi +``` + +### Send commands to script + +Send commands to running server script. First lists /dev directory, +second output aloha and third one terminates script. + +``` +./send_command ls +./send_command aloha +./send_command stop +``` + +_send_command.sh_ +```bash +#!/bin/sh +QUEUE_NAME=/music-player-daemon +SEND_CMD() +{ + ./mqueue_push -m $QUEUE_NAME -p "$1" +} +echo "Send command to daemon" +if [ -f "/dev/mqueue$QUEUE_NAME" ]; then + SEND_CMD $1 +else + echo "No queue to send" +fi +``` + +## Source of example utilities for using mqueue + +All source located at [http://git.main.lv/cgit.cgi/mqueue_examples.git/](http://git.main.lv/cgit.cgi/mqueue_examples.git/) + +When compiling **-lrt** flag required + +### mqueue_create.c - Create mqueue + +_mqueue_create.c_ +```c +#include +#include +#include +#include +#include +#include +#include + +typedef struct { + char *f_mqname; + int f_verbose; + int f_helper; +} mqueue_params; + +mqueue_params g_params; + +void helper(char *progname) +{ + printf("Usage: %s [OPTS]\n\n" + "Version: 0.0.1 \n" + "-m mqueue name\n" + "-v verbose output\n" + "-h help options\n" + "\n" + , progname); +} + +#define full_mqpath_len 1024 + + +int main(int argc, char **argv) +{ + //stack params + int c,err; + int i; + int mq_fd; + char full_mqpath[full_mqpath_len]; + + + memset(&g_params,0,sizeof(g_params)); + + //process arguments + while ((c = getopt(argc, argv, "m:vh")) != -1) + { + switch (c) + { + case 'm': + g_params.f_mqname = optarg; + break; + case 'v': + g_params.f_verbose = 1; + break; + case 'h': + default: + helper(argv[0]); + exit(1); + } + } + + if (g_params.f_helper) + { + helper(argv[0]); + return 0; + } + + //set mqueue name + if (g_params.f_mqname == NULL) + { + g_params.f_mqname = "/stack-0"; + } + + //compose string + memset(&full_mqpath[0], 0, full_mqpath_len); + snprintf(&full_mqpath[0], full_mqpath_len-1, "%s", g_params.f_mqname); + if (g_params.f_verbose) + { + printf("Full path: %s\n",&full_mqpath); + } + + mq_fd = mq_open(g_params.f_mqname,O_RDWR | O_CREAT, 0666, NULL); + err = errno; + if (mq_fd == -1) + { + if (g_params.f_verbose) + { + printf("ERROR:%d,%s\n",err,strerror(err)); + } + return -1; + } + printf("%s\n",g_params.f_mqname); + + close(mq_fd); + + return 0; +} +``` + +### mqueue_push.c - Push values to mqueue + +_mqueue_push.c_ +```c +#include +#include +#include +#include +#include +#include +#include + +typedef struct { + char *f_mqname; + char *f_push; + int f_verbose; + int f_helper; +} mqueue_params; + +mqueue_params g_params; + +void helper(char *progname) +{ + printf("Usage: %s [OPTS] VALUE\n\n" + "Version: 0.0.1 \n" + "-m mqueue name\n" + "-v verbose output\n" + "-p push value\n" + "-h help options\n" + "\n" + , progname); +} + +#define full_mqpath_len 1024 + +int main(int argc, char **argv) +{ + //stack params + int c,err; + int i; + int mq_fd; + char full_mqpath[full_mqpath_len]; + int prio=10; + char *push_val="NONE"; + int push_size=strlen(push_val); + + + memset(&g_params,0,sizeof(g_params)); + + //process arguments + while ((c = getopt(argc, argv, "m:vhp:")) != -1) + { + switch (c) + { + case 'm': + g_params.f_mqname = optarg; + break; + case 'v': + g_params.f_verbose = 1; + break; + case 'p': + g_params.f_push = optarg; + break; + case 'h': + default: + helper(argv[0]); + exit(1); + } + } + + if (g_params.f_helper) + { + helper(argv[0]); + return 0; + } + + //set mqueue name + if (g_params.f_mqname == NULL) + { + g_params.f_mqname = "/stack-0"; + } + + //compose string + memset(&full_mqpath[0], 0, full_mqpath_len); + snprintf(&full_mqpath[0], full_mqpath_len-1, "%s", g_params.f_mqname); + if (g_params.f_verbose) + { + printf("Full path: %s\n",&full_mqpath); + } + + mq_fd = mq_open(g_params.f_mqname,O_RDWR | O_CREAT, 0666, NULL); + err = errno; + if (mq_fd == -1) + { + if (g_params.f_verbose) + { + printf("ERROR:%d,%s\n",err,strerror(err)); + } + return -1; + } + printf("%s\n",g_params.f_mqname); + + if (g_params.f_push != NULL) + { + push_val = g_params.f_push; + push_size = strlen(push_val); + } + + mq_send(mq_fd, push_val, push_size, prio); + + close(mq_fd); + + return 0; +} +``` + +### mqueue_pop.c - Get value from mqueue + +_mqueue_pop.c_ +```c +#include +#include +#include +#include +#include +#include +#include + +typedef struct { + char *f_mqname; + int f_verbose; + int f_helper; +} mqueue_params; + +mqueue_params g_params; + +void helper(char *progname) +{ + printf("Usage: %s [OPTS] VALUE\n\n" + "Version: 0.0.1 \n" + "-m mqueue name\n" + "-v verbose output\n" + "-h help options\n" + "\n" + , progname); +} + +#define full_mqpath_len 1024 + +int main(int argc, char **argv) +{ + //stack params + int ret=0; + int c,err; + int i; + int mq_fd; + char full_mqpath[full_mqpath_len]; + int prio; + struct mq_attr attr; + char *pop_val=NULL; + int pop_size; + + + memset(&g_params,0,sizeof(g_params)); + + //process arguments + while ((c = getopt(argc, argv, "m:vh")) != -1) + { + switch (c) + { + case 'm': + g_params.f_mqname = optarg; + break; + case 'v': + g_params.f_verbose = 1; + break; + case 'h': + default: + helper(argv[0]); + exit(1); + } + } + + if (g_params.f_helper) + { + helper(argv[0]); + return 0; + } + + //set mqueue name + if (g_params.f_mqname == NULL) + { + g_params.f_mqname = "/stack-0"; + } + + //compose string + memset(&full_mqpath[0], 0, full_mqpath_len); + snprintf(&full_mqpath[0], full_mqpath_len-1, "%s", g_params.f_mqname); + if (g_params.f_verbose) + { + printf("Full path: %s\n",&full_mqpath); + } + + mq_fd = mq_open(g_params.f_mqname,O_RDWR | O_CREAT, 0666, NULL); + err = errno; + if (mq_fd == -1) + { + if (g_params.f_verbose) + { + printf("ERROR:%d,%s\n",err,strerror(err)); + } + return -1; + } + //printf("%s\n",g_params.f_mqname); + + mq_getattr(mq_fd, &attr); + + pop_val = malloc(attr.mq_msgsize+1); + memset(pop_val, 0, attr.mq_msgsize+1); + + if (attr.mq_curmsgs > 0) + { + pop_size = mq_receive(mq_fd, pop_val, attr.mq_msgsize, &prio); + printf("%s\n",pop_val); + } else { + ret = -1; + } + + close(mq_fd); + free(pop_val); + + return ret; +} +``` + +### mqueue_remove.c - Remove mqueue + +_mqueue_remove.c_ +```c +#include +#include +#include +#include +#include +#include +#include + +typedef struct { + char *f_mqname; + int f_verbose; + int f_helper; +} mqueue_params; + +mqueue_params g_params; + +void helper(char *progname) +{ + printf("Usage: %s [OPTS] VALUE\n\n" + "Version: 0.0.1 \n" + "-m mqueue name\n" + "-v verbose output\n" + "-h help options\n" + "\n" + , progname); +} + +#define full_mqpath_len 1024 + +int main(int argc, char **argv) +{ + //stack params + int ret=0; + int c,err; + int i; + int mq_fd; + char full_mqpath[full_mqpath_len]; + + + memset(&g_params,0,sizeof(g_params)); + + //process arguments + while ((c = getopt(argc, argv, "m:vh")) != -1) + { + switch (c) + { + case 'm': + g_params.f_mqname = optarg; + break; + case 'v': + g_params.f_verbose = 1; + break; + case 'h': + default: + helper(argv[0]); + exit(1); + } + } + + if (g_params.f_helper) + { + helper(argv[0]); + return 0; + } + + //set mqueue name + if (g_params.f_mqname == NULL) + { + g_params.f_mqname = "/stack-0"; + } + + //compose string + memset(&full_mqpath[0], 0, full_mqpath_len); + snprintf(&full_mqpath[0], full_mqpath_len-1, "%s", g_params.f_mqname); + if (g_params.f_verbose) + { + printf("Full path: %s\n",&full_mqpath); + } + + mq_unlink(g_params.f_mqname); + + close(mq_fd); + + return ret; +} +``` + +### mqueue_info.c - Get stats from mqueue + +_mqueue_info.c_ +```c +#include +#include +#include +#include +#include +#include +#include + +typedef struct { + char *f_mqname; + int f_verbose; + int f_helper; +} mqueue_params; + +mqueue_params g_params; + +void helper(char *progname) +{ + printf("Usage: %s [OPTS] VALUE\n\n" + "Version: 0.0.1 \n" + "-m mqueue name\n" + "-v verbose output\n" + "-h help options\n" + "\n" + , progname); +} + +#define full_mqpath_len 1024 + +int main(int argc, char **argv) +{ + //stack params + int ret=0; + int c,err; + int i; + int mq_fd; + char full_mqpath[full_mqpath_len]; + int prio; + struct mq_attr attr; + char *pop_val=NULL; + int pop_size; + + + memset(&g_params,0,sizeof(g_params)); + + //process arguments + while ((c = getopt(argc, argv, "m:vh")) != -1) + { + switch (c) + { + case 'm': + g_params.f_mqname = optarg; + break; + case 'v': + g_params.f_verbose = 1; + break; + case 'h': + default: + helper(argv[0]); + exit(1); + } + } + + if (g_params.f_helper) + { + helper(argv[0]); + return 0; + } + + //set mqueue name + if (g_params.f_mqname == NULL) + { + g_params.f_mqname = "/stack-0"; + } + + //compose string + memset(&full_mqpath[0], 0, full_mqpath_len); + snprintf(&full_mqpath[0], full_mqpath_len-1, "%s", g_params.f_mqname); + if (g_params.f_verbose) + { + printf("Full path: %s\n",&full_mqpath); + } + + mq_fd = mq_open(g_params.f_mqname,O_RDWR | O_CREAT, 0666, NULL); + err = errno; + if (mq_fd == -1) + { + if (g_params.f_verbose) + { + printf("ERROR:%d,%s\n",err,strerror(err)); + } + return -1; + } + + mq_getattr(mq_fd, &attr); + + printf("%d\n",attr.mq_curmsgs); + + + close(mq_fd); + + return ret; +} +``` + + + +## Links + +[1] [https://www.man7.org/linux/man-pages/man7/mq_overview.7.html](https://www.man7.org/linux/man-pages/man7/mq_overview.7.html) +[2] [https://en.wikipedia.org/wiki/Message_queue](https://en.wikipedia.org/wiki/Message_queue) -- cgit v1.2.3