222 lines
4.3 KiB
Text
222 lines
4.3 KiB
Text
@code_type c .c
|
|
@comment_type /* %s */
|
|
|
|
@title syrup
|
|
|
|
@s Introduction
|
|
Using syrup is rather straight forward. The syntax is as follows: `syrup COMMAND`.
|
|
|
|
The program is also simple. Here is an outline.
|
|
|
|
--- syrup.c
|
|
@{copyright notice}
|
|
@{c headers}
|
|
@{posix headers}
|
|
|
|
@{helper function}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
@{variables local to main}
|
|
|
|
if (argc < 2) {
|
|
die("%s: not enough arguments\n", argv[0]);
|
|
}
|
|
|
|
@{open socket}
|
|
@{send message}
|
|
@{receive and print reply}
|
|
@{cleanup}
|
|
}
|
|
---
|
|
|
|
As we can already see we will need to include `stdio.h` and `stdlib.h`.
|
|
|
|
--- c headers
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
---
|
|
|
|
@s Copyright
|
|
|
|
This project is licensed under the Mozilla Public License Version 2.0. Please read LICENSE for more details.
|
|
|
|
--- copyright notice
|
|
/* Copyright (c), Niclas Meyer <niclas@countingsort.com>
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
* You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
*/
|
|
---
|
|
|
|
@s Creating the socket object
|
|
|
|
This is rather straight forward. We setup all the things in our socket object and then connect.
|
|
|
|
We're going to store the file descriptor and the `sockaddr` local to main.
|
|
|
|
--- variables local to main
|
|
int sock_fd;
|
|
struct sockaddr_un sock_addr;
|
|
---
|
|
|
|
This requires including the following headers.
|
|
|
|
--- posix headers
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
---
|
|
|
|
Now it's finally time to fill stuff into our `sockaddr`.
|
|
|
|
--- open socket
|
|
if ((sock_fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
|
|
die("%s: failed to open socket\n", argv[0]);
|
|
}
|
|
|
|
sock_addr.sun_family = AF_UNIX;
|
|
|
|
@{get socket path}
|
|
|
|
if (connect(sock_fd, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) < 0) {
|
|
die("%s: failed to connect to socket\n", argv[0]);
|
|
}
|
|
---
|
|
|
|
@s Get the socket path
|
|
|
|
It's important that we open the correct socket.
|
|
This might be set via an environment variable to allow maximum flexibility, while still keeping a trivial interface.
|
|
|
|
We're going to store the path in `sock_path`, local to main.
|
|
|
|
--- variables local to main +=
|
|
const char *sock_path;
|
|
---
|
|
|
|
We check if `WMAFFLE_SOCKET` is set, and use it, or default to `/tmp/wmaffle.sock`.
|
|
|
|
--- get socket path
|
|
sock_path = getenv("WMAFFLE_SOCKET");
|
|
|
|
if (sock_path) {
|
|
strncpy(sock_addr.sun_path, sock_path, sizeof(sock_addr.sun_path));
|
|
} else {
|
|
strncpy(sock_addr.sun_path, "/tmp/wmaffle.sock", sizeof(sock_addr.sun_path));
|
|
}
|
|
---
|
|
|
|
For this we need `string.h`.
|
|
|
|
--- c headers +=
|
|
#include <string.h>
|
|
---
|
|
|
|
@s Send our message
|
|
|
|
This actually consists of two steps.
|
|
|
|
--- send message
|
|
@{generate message from argv}
|
|
@{send generated message}
|
|
---
|
|
|
|
@s Generate message
|
|
|
|
Pretty straight forward. We need a buffer and then we just append all arguments, beginning with `argv[1]`, seperated by spaces.
|
|
|
|
--- variables local to main +=
|
|
char msg[BUFSIZ];
|
|
size_t msg_len;
|
|
---
|
|
|
|
--- generate message from argv
|
|
msg_len = 0;
|
|
|
|
for (int i = 1; i < argc; i++) {
|
|
msg_len += (size_t)snprintf(msg + msg_len, sizeof(msg), "%s ", argv[i]);
|
|
}
|
|
|
|
msg_len -= 1; // Remove trailing space
|
|
---
|
|
|
|
@s Actually send the message
|
|
|
|
This is straight forward enough. We just `send` the message.
|
|
|
|
--- send generated message
|
|
if (send(sock_fd, msg, msg_len, 0) < 0) {
|
|
die("%s: failed to send message\n", argv[0]);
|
|
}
|
|
---
|
|
|
|
@s Handling the reply
|
|
|
|
Some commands return a string to print. If there's none, we'll just receive a `\0`.
|
|
This is pretty much stolen from baskerville.
|
|
|
|
--- receive and print reply
|
|
struct pollfd fds[] = {
|
|
{ sock_fd, POLLIN, 0 },
|
|
{ STDOUT_FILENO, POLLHUP, 0 },
|
|
};
|
|
|
|
while (poll(fds, 2, -1) > 0) {
|
|
if (fds[1].revents & (POLLERR | POLLHUP)) {
|
|
break;
|
|
}
|
|
|
|
if (fds[0].revents & POLLIN) {
|
|
if ((msg_len = recv(sock_fd, msg, sizeof(msg) - 1, 0)) > 0) {
|
|
msg[msg_len] = '\0';
|
|
if (msg_len == 1) {
|
|
break;
|
|
}
|
|
|
|
printf("%s", msg);
|
|
fflush(stdout);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
---
|
|
|
|
This requires us to include `poll.h` and `unistd.h`.
|
|
|
|
--- posix headers +=
|
|
#include <poll.h>
|
|
#include <unistd.h>
|
|
---
|
|
|
|
@s Cleanup
|
|
|
|
Not much to do here. Just close the socket.
|
|
|
|
--- cleanup
|
|
close(sock_fd);
|
|
---
|
|
|
|
@s Helper function
|
|
|
|
We used `die` the whole time. Time to make it a thing already.
|
|
|
|
--- helper function
|
|
static void
|
|
die(char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
vfprintf(stderr, fmt, ap);
|
|
va_end(ap);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
---
|
|
|
|
Note that this requires the `stdarg.h` header.
|
|
|
|
--- c headers +=
|
|
#include <stdarg.h>
|
|
---
|