@code_type c .c @comment_type /* %s */ @title seed @s Introduction Using seed is rather straight forward. The syntax is as follows: `seed COMMAND`. The program is also simple. Here is an outline. --- seed.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 #include --- @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 * * 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 #include --- 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 `KIWMI_SOCKET` is set, and use it, or default to `/tmp/kiwmi.sock`. --- get socket path sock_path = getenv("KIWMI_SOCKET"); if (sock_path) { strncpy(sock_addr.sun_path, sock_path, sizeof(sock_addr.sun_path)); } else { strncpy(sock_addr.sun_path, "/tmp/kiwmi.sock", sizeof(sock_addr.sun_path)); } --- For this we need `string.h`. --- c headers += #include --- @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]`, separated 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 #include --- @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 ---