Skip to content

Commit 2a53966

Browse files
committed
feat: add initial libhopper client library
1 parent 42726fe commit 2a53966

File tree

2 files changed

+150
-2
lines changed

2 files changed

+150
-2
lines changed

client/lib.c

Lines changed: 118 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,122 @@
1+
#include <errno.h>
2+
#include <fcntl.h>
3+
#include <limits.h>
14
#include <stdio.h>
5+
#include <stdlib.h>
6+
#include <sys/file.h>
7+
#include <sys/stat.h>
8+
#include <unistd.h>
29

310
#include "hopper/hopper.h"
411

5-
void hello() { printf("hello world\n"); }
12+
int get_open_flags(int flags) {
13+
int open_flags = 0;
14+
15+
if (flags & HOPPER_IN)
16+
open_flags |= O_WRONLY;
17+
if (flags & HOPPER_OUT)
18+
open_flags |= O_RDONLY;
19+
if (flags & HOPPER_NONBLOCK)
20+
open_flags |= O_NONBLOCK;
21+
22+
return open_flags;
23+
}
24+
25+
char *get_pipe_path(struct hopper_pipe *pipe) {
26+
char *path = (char *)malloc(sizeof(char) * PATH_MAX);
27+
const char *suffix = (pipe->flags & HOPPER_IN ? "in" : "out");
28+
29+
// {HOPPER}/{ENDPOINT}/{NAME}.{TYPE}
30+
sprintf(path, "%s/%s/%s.%s", pipe->hopper, pipe->endpoint, pipe->name,
31+
suffix);
32+
33+
return path;
34+
}
35+
36+
int hopper_open(struct hopper_pipe *pipe) {
37+
int res = 0;
38+
39+
if (pipe->name == NULL || pipe->endpoint == NULL) {
40+
// pipe and endpoint name are (obviously) required
41+
errno = EINVAL;
42+
return -1;
43+
}
44+
45+
if ((pipe->flags & HOPPER_IN && pipe->flags & HOPPER_OUT) ||
46+
(pipe->flags & ~HOPPER_IN && pipe->flags & ~HOPPER_OUT)) {
47+
// either both or none of the input/output flags are set
48+
errno = EINVAL;
49+
return -1;
50+
}
51+
52+
if (pipe->hopper == NULL) {
53+
// hopper path isn't overridden, get from environment
54+
pipe->hopper = getenv("HOPPER_PATH");
55+
56+
if (pipe->hopper == NULL) {
57+
// still don't have a valid path, can't open pipe
58+
errno = ENOENT;
59+
return -1;
60+
}
61+
}
62+
63+
char *pipe_path = get_pipe_path(pipe);
64+
if (mknod(pipe_path, S_IFIFO | 0660, 0) < 0 && errno != EEXIST) {
65+
// mknod failed in some way, preserve errno
66+
res = -1;
67+
goto cleanup;
68+
}
69+
70+
int open_flags = get_open_flags(pipe->flags);
71+
int fd = open(pipe_path, open_flags);
72+
if (fd < 0) {
73+
// preserve errno if open fails
74+
res = -1;
75+
goto cleanup;
76+
}
77+
78+
// acquire a file lock on the fifo, we don't want other things using it
79+
if (flock(fd, (pipe->flags & HOPPER_IN ? LOCK_EX : LOCK_SH) | LOCK_NB) !=
80+
0) {
81+
if (errno == EWOULDBLOCK)
82+
errno = EBUSY; // this makes more sense for clients
83+
84+
res = -1;
85+
close(fd);
86+
goto cleanup;
87+
}
88+
89+
pipe->fd = fd;
90+
91+
cleanup:
92+
free(pipe_path);
93+
if (res != 0)
94+
pipe->fd = -1;
95+
96+
return res;
97+
}
98+
99+
void hopper_close(struct hopper_pipe *pipe) {
100+
if (pipe->fd == -1)
101+
return;
102+
103+
flock(pipe->fd, LOCK_UN);
104+
close(pipe->fd);
105+
106+
pipe->fd = -1;
107+
}
108+
109+
ssize_t hopper_read(struct hopper_pipe *pipe, void *dst, size_t len) {
110+
ssize_t res = read(pipe->fd, dst, len);
111+
if (res < 0 && errno == EWOULDBLOCK)
112+
return 0; // EWOULDBLOCK isn't an error for non-block pipes
113+
return res;
114+
}
115+
116+
ssize_t hopper_write(struct hopper_pipe *pipe, void *src, size_t len) {
117+
ssize_t res = write(pipe->fd, src, len);
118+
if (res < 0 && errno == EWOULDBLOCK)
119+
return 0; // EWOULDBLOCK isn't an error for non-block pipes
120+
return res;
121+
}
122+

include/hopper/hopper.h

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,37 @@
11
#ifndef hopper_h_INCLUDED
22
#define hopper_h_INCLUDED
33

4-
void hello();
4+
#include <unistd.h>
5+
6+
#define HOPPER_IN 1
7+
#define HOPPER_OUT 2
8+
#define HOPPER_NONBLOCK 4
9+
10+
/// Structure representing a Hopper pipe
11+
struct hopper_pipe {
12+
const char *name;
13+
const char *endpoint;
14+
const char *hopper;
15+
int fd;
16+
int flags;
17+
};
18+
19+
/// Open a new Hopper pipe specified by `pipe`.
20+
/// Hopper location will be determined from the `HOPPER_PATH` environment
21+
/// variable, or can be overridden with the `hopper_pipe.hopper` string.
22+
/// -1 is returned on error, and errno is set.
23+
int hopper_open(struct hopper_pipe *pipe);
24+
25+
/// Close a Hopper pipe previously opened by `hopper_open_pipe`.
26+
void hopper_close(struct hopper_pipe *pipe);
27+
28+
/// Read up to `len` bytes from a Hopper pipe. Value returned indicates
29+
/// the number of bytes read. -1 is returned on error and errno is set.
30+
ssize_t hopper_read(struct hopper_pipe *pipe, void *dst, size_t len);
31+
32+
/// Write up to `len` bytes into a Hopper pipe. Value returned indicates
33+
/// the number of bytes written. -1 is returned on error and errno is set.
34+
ssize_t hopper_write(struct hopper_pipe *pipe, void *src, size_t len);
535

636
#endif // hopper_h_INCLUDED
37+

0 commit comments

Comments
 (0)