Skip to content

Commit

Permalink
Add support for external image data source
Browse files Browse the repository at this point in the history
Allows to load image by reading stdout from external command.

Relates to #145, #146.

Signed-off-by: Artem Senichev <[email protected]>
  • Loading branch information
artemsen committed Jul 4, 2024
1 parent 37897a5 commit d186ade
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 10 deletions.
3 changes: 3 additions & 0 deletions extra/swayimg.1
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ same directory as the image being loaded. This behavior can be changed with
the \fBlist.all\fR parameter in the config file.
.PP
Use '-' as \fIFILE\fR to read image data from stdin.
.PP
Use prefix 'exec://' to get image data from stdout printed by external command,
e.g. `swayimg "exec://wget -qO- https://www.kernel.org/theme/images/logos/tux.png"`.
.\" ****************************************************************************
.\" Options
.\" ****************************************************************************
Expand Down
8 changes: 5 additions & 3 deletions src/imagelist.c
Original file line number Diff line number Diff line change
Expand Up @@ -303,11 +303,13 @@ size_t image_list_init(const char** sources, size_t num)
add_dir(".", ctx.recursive);
} else if (num == 1 && strcmp(sources[0], ALT_STDIN_FILE_NAME) == 0) {
// pipe mode
add_file(STDIN_FILE_NAME);
add_file(LDRSRC_STDIN);
} else {
for (size_t i = 0; i < num; ++i) {
struct stat file_stat;
if (stat(sources[i], &file_stat) == 0) {
if (strncmp(sources[i], LDRSRC_EXEC, LDRSRC_EXEC_LEN) == 0) {
add_entry(sources[i]);
} else if (stat(sources[i], &file_stat) == 0) {
if (S_ISDIR(file_stat.st_mode)) {
add_dir(sources[i], ctx.recursive);
} else {
Expand Down Expand Up @@ -362,7 +364,7 @@ size_t image_list_find(const char* source)
}
// handle alternate name for stdio
if (strcmp(source, ALT_STDIN_FILE_NAME) == 0) {
source = STDIN_FILE_NAME;
source = LDRSRC_STDIN;
}

for (size_t i = 0; i < ctx.size; ++i) {
Expand Down
54 changes: 48 additions & 6 deletions src/loader.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>

#ifdef HAVE_INOTIFY
Expand Down Expand Up @@ -401,11 +402,12 @@ static enum loader_status image_from_file(struct image* img, const char* file)
}

/**
* Load image from stdin.
* Load image from stream file (stdin).
* @param img destination image
* @param fd file descriptor for read
* @return loader status
*/
static enum loader_status image_from_stdin(struct image* img)
static enum loader_status image_from_stream(struct image* img, int fd)
{
enum loader_status status = ldr_ioerror;
uint8_t* data = NULL;
Expand All @@ -425,7 +427,7 @@ static enum loader_status image_from_stdin(struct image* img)
capacity = new_capacity;
}

rc = read(STDIN_FILENO, data + size, capacity - size);
rc = read(fd, data + size, capacity - size);
if (rc == 0) {
status = image_from_memory(img, data, size);
break;
Expand All @@ -440,6 +442,44 @@ static enum loader_status image_from_stdin(struct image* img)
return status;
}

/**
* Load image from stdout printed by external command.
* @param img destination image
* @param cmd execution command to get stdout data
* @return loader status
*/
static enum loader_status image_from_exec(struct image* img, const char* cmd)
{
enum loader_status status = ldr_ioerror;
int pfd[2];
pid_t pid;

if (pipe(pfd) == -1) {
abort();
return ldr_ioerror;
}

pid = fork();
if (pid == -1) {
abort();
return ldr_ioerror;
}

if (pid == 0) { // child
dup2(pfd[1], STDOUT_FILENO);
close(pfd[1]);
execlp("sh", "/bin/sh", "-c", cmd, NULL);
exit(1);
} else { // parent
close(pfd[1]);
status = image_from_stream(img, pfd[0]);
close(pfd[0]);
waitpid(pid, NULL, 0);
}

return status;
}

struct image* loader_load_image(const char* source, enum loader_status* status)
{
struct image* img = NULL;
Expand All @@ -453,15 +493,17 @@ struct image* loader_load_image(const char* source, enum loader_status* status)
// save image source info
img->source = str_dup(source, NULL);
img->name = strrchr(img->source, '/');
if (!img->name) {
if (!img->name || strcmp(img->name, "/") == 0) {
img->name = img->source;
} else {
++img->name; // skip slash
}

// decode image
if (strcmp(source, STDIN_FILE_NAME) == 0) {
rc = image_from_stdin(img);
if (strcmp(source, LDRSRC_STDIN) == 0) {
rc = image_from_stream(img, STDIN_FILENO);
} else if (strncmp(source, LDRSRC_EXEC, LDRSRC_EXEC_LEN) == 0) {
rc = image_from_exec(img, source + LDRSRC_EXEC_LEN);
} else {
rc = image_from_file(img, source);
}
Expand Down
6 changes: 5 additions & 1 deletion src/loader.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@
#include "image.h"

// File name used for image, that is read from stdin through pipe
#define STDIN_FILE_NAME "{STDIN}"
#define LDRSRC_STDIN "stdin://"

// Special prefix used to load images from external command output
#define LDRSRC_EXEC "exec://"
#define LDRSRC_EXEC_LEN (sizeof(LDRSRC_EXEC) - 1)

/** Loader status. */
enum loader_status {
Expand Down

0 comments on commit d186ade

Please sign in to comment.