PatchworkOS  966e257
A non-POSIX operating system.
Loading...
Searching...
No Matches
socket.c
Go to the documentation of this file.
1#include "socket.h"
2
3#include "socket_family.h"
4#include <kernel/fs/ctl.h>
5#include <kernel/fs/file.h>
6#include <kernel/fs/mount.h>
7#include <kernel/fs/path.h>
10#include <kernel/sched/wait.h>
11#include <kernel/sync/lock.h>
12#include <kernel/sync/mutex.h>
13#include <kernel/sync/rwmutex.h>
14
15#include <errno.h>
16#include <stdio.h>
17#include <stdlib.h>
18#include <sys/io.h>
19
21{
22 socket_t* sock = file->inode->private;
23 if (sock == NULL || sock->family == NULL)
24 {
25 errno = EINVAL;
26 return ERR;
27 }
28
29 file->private = REF(sock);
30 return 0;
31}
32
34{
35 socket_t* sock = file->private;
36 if (sock == NULL)
37 {
38 return;
39 }
40
41 UNREF(sock);
42}
43
44static uint64_t socket_data_read(file_t* file, void* buf, size_t count, uint64_t* offset)
45{
46 socket_t* sock = file->private;
47 if (sock == NULL || sock->family == NULL)
48 {
49 errno = EINVAL;
50 return ERR;
51 }
52
53 if (sock->family->ops->recv == NULL)
54 {
55 errno = ENOSYS;
56 return ERR;
57 }
58
60
61 if (sock->currentState != SOCKET_CONNECTED)
62 {
64 return ERR;
65 }
66
67 return sock->family->ops->recv(sock, buf, count, offset, file->mode);
68}
69
70static uint64_t socket_data_write(file_t* file, const void* buf, size_t count, uint64_t* offset)
71{
72 socket_t* sock = file->private;
73 if (sock == NULL || sock->family == NULL)
74 {
75 errno = EINVAL;
76 return ERR;
77 }
78
79 if (sock->family->ops->send == NULL)
80 {
81 errno = ENOSYS;
82 return ERR;
83 }
84
86
87 if (sock->currentState != SOCKET_CONNECTED)
88 {
90 return ERR;
91 }
92
93 return sock->family->ops->send(sock, buf, count, offset, file->mode);
94}
95
97{
98 socket_t* sock = file->private;
99 if (sock == NULL || sock->family == NULL)
100 {
101 errno = EINVAL;
102 return NULL;
103 }
104
105 if (sock->family->ops->poll == NULL)
106 {
107 errno = ENOSYS;
108 return NULL;
109 }
110
111 return sock->family->ops->poll(sock, revents);
112}
113
116 .close = socket_data_close,
117 .read = socket_data_read,
118 .write = socket_data_write,
119 .poll = socket_data_poll,
120};
121
122static uint64_t socket_ctl_bind(file_t* file, uint64_t argc, const char** argv)
123{
124 (void)argc; // Unused
125
126 socket_t* sock = file->inode->private;
127 if (sock == NULL || sock->family == NULL)
128 {
129 errno = EINVAL;
130 return ERR;
131 }
132
133 if (sock->family->ops->bind == NULL)
134 {
135 errno = ENOSYS;
136 return ERR;
137 }
138
140 {
141 return ERR;
142 }
143
144 uint64_t result = sock->family->ops->bind(sock, argv[1]);
145 socket_end_transition(sock, result);
146 return result;
147}
148
149static uint64_t socket_ctl_listen(file_t* file, uint64_t argc, const char** argv)
150{
151 socket_t* sock = file->inode->private;
152 if (sock == NULL || sock->family == NULL)
153 {
154 errno = EINVAL;
155 return ERR;
156 }
157
158 if (sock->family->ops->listen == NULL)
159 {
160 errno = ENOSYS;
161 return ERR;
162 }
163
164 uint32_t backlog = 128;
165 if (argc > 1)
166 {
167 backlog = atol(argv[1]);
168 }
169
171 {
172 return ERR;
173 }
174
175 uint64_t result = sock->family->ops->listen(sock, backlog);
176 socket_end_transition(sock, result);
177 return result;
178}
179
180static uint64_t socket_ctl_connect(file_t* file, uint64_t argc, const char** argv)
181{
182 (void)argc; // Unused
183
184 socket_t* sock = file->inode->private;
185 if (sock == NULL || sock->family == NULL)
186 {
187 errno = EINVAL;
188 return ERR;
189 }
190
191 if (sock->family->ops->connect == NULL)
192 {
193 errno = ENOSYS;
194 return ERR;
195 }
196
198 {
199 return ERR;
200 }
201
202 uint64_t result = sock->family->ops->connect(sock, argv[1]);
203 if (result == ERR)
204 {
206 return result;
207 }
208
209 bool notFinished = (file->mode & MODE_NONBLOCK) && (result == ERR && errno == EINPROGRESS);
210 if (notFinished) // Non blocking and not yet connected.
211 {
212 socket_end_transition(sock, 0);
213
215 return ERR;
216 }
217
218 // Connection finished immediately.
220
221 socket_end_transition(sock, 0);
222 return 0;
223}
224
226 {
227 {"bind", socket_ctl_bind, 2, 2},
228 {"listen", socket_ctl_listen, 1, 2},
229 {"connect", socket_ctl_connect, 2, 2},
230 {0},
231 });
232
234{
235 socket_t* sock = file->inode->private;
236 if (sock == NULL || sock->family == NULL)
237 {
238 errno = EINVAL;
239 return ERR;
240 }
241
242 if (sock->family->ops->accept == NULL)
243 {
244 errno = ENOSYS;
245 return ERR;
246 }
247
249
250 if (sock->currentState != SOCKET_LISTENING)
251 {
252 errno = EINVAL;
253 return ERR;
254 }
255
256 socket_t* newSock = socket_new(sock->family, sock->type);
257 if (newSock == NULL)
258 {
259 return ERR;
260 }
261
263 {
264 return ERR;
265 }
266
267 if (sock->family->ops->accept(sock, newSock, file->mode) == ERR)
268 {
269 socket_end_transition(newSock, ERR);
270 UNREF(newSock);
271 return ERR;
272 }
273
275
276 socket_end_transition(newSock, 0);
277 file->private = newSock;
278 return 0;
279}
280
282{
283 socket_t* sock = file->private;
284 if (sock == NULL)
285 {
286 return;
287 }
288
289 UNREF(sock);
290}
291
294 .close = socket_accept_close,
295 .read = socket_data_read,
296 .write = socket_data_write,
297 .poll = socket_data_poll,
298};
299
300static void socket_inode_cleanup(inode_t* inode)
301{
302 socket_t* sock = inode->private;
303 if (sock == NULL)
304 {
305 return;
306 }
307
308 UNREF(sock);
309}
310
314
315/**
316 * Will only be called when the socket reference count reaches 0. Meaning the socket must be unmounted first.
317 */
318static void socket_free(socket_t* sock)
319{
320 if (sock == NULL)
321 {
322 return;
323 }
324
325 if (sock->family != NULL)
326 {
327 sock->family->ops->deinit(sock);
328 }
329
330 rwmutex_deinit(&sock->mutex);
331 free(sock);
332}
333
334static void socket_unmount(superblock_t* superblock)
335{
336 if (superblock == NULL)
337 {
338 return;
339 }
340
341 socket_t* sock = superblock->private;
342 if (sock == NULL)
343 {
344 return;
345 }
346 UNREF(sock->ctlFile);
347 UNREF(sock->dataFile);
348 UNREF(sock->acceptFile);
349 UNREF(sock);
350 superblock->private = NULL;
351}
352
356
358{
359 if (family == NULL)
360 {
361 errno = EINVAL;
362 return NULL;
363 }
364
365 socket_t* sock = calloc(1, sizeof(socket_t));
366 if (sock == NULL)
367 {
368 errno = ENOMEM;
369 return NULL;
370 }
371
372 ref_init(&sock->ref, socket_free);
373 snprintf(sock->id, sizeof(sock->id), "%llu", atomic_fetch_add(&family->newId, 1));
374 sock->family = family;
375 sock->type = type;
376 sock->private = NULL;
377 rwmutex_init(&sock->mutex);
378 sock->currentState = SOCKET_NEW;
379 sock->nextState = SOCKET_NEW;
380
381 if (family->ops->init(sock) == ERR)
382 {
383 rwmutex_deinit(&sock->mutex);
384 free(sock);
385 return NULL;
386 }
387
388 path_t familyDir = PATH_EMPTY;
389 if (socket_family_get_dir(family, &familyDir) == ERR)
390 {
391 family->ops->deinit(sock);
392 rwmutex_deinit(&sock->mutex);
393 free(sock);
394 return NULL;
395 }
396
399 path_put(&familyDir);
400 if (mount == NULL)
401 {
402 family->ops->deinit(sock);
403 rwmutex_deinit(&sock->mutex);
404 free(sock);
405 return NULL;
406 }
407 mount->superblock->private = REF(sock);
408 UNREF(mount);
409
410 sock->ctlFile = sysfs_file_new(mount->source, "ctl", &inodeOps, &ctlOps, REF(sock));
411 if (sock->ctlFile == NULL)
412 {
413 family->ops->deinit(sock);
415 rwmutex_deinit(&sock->mutex);
416 free(sock);
417 return NULL;
418 }
419
420 sock->dataFile = sysfs_file_new(mount->source, "data", &inodeOps, &dataOps, REF(sock));
421 if (sock->dataFile == NULL)
422 {
423 family->ops->deinit(sock);
425 UNREF(sock->ctlFile);
426 rwmutex_deinit(&sock->mutex);
427 free(sock);
428 return NULL;
429 }
430
431 sock->acceptFile = sysfs_file_new(mount->source, "accept", &inodeOps, &acceptOps, REF(sock));
432 if (sock->acceptFile == NULL)
433 {
434 family->ops->deinit(sock);
436 UNREF(sock->ctlFile);
437 UNREF(sock->dataFile);
438 rwmutex_deinit(&sock->mutex);
439 free(sock);
440 return NULL;
441 }
442
443 return sock;
444}
445
447 [SOCKET_NEW] =
448 {
449 [SOCKET_BOUND] = true,
450 [SOCKET_CONNECTING] = true,
451 [SOCKET_CLOSED] = true,
452 },
453 [SOCKET_BOUND] =
454 {
455 [SOCKET_LISTENING] = true,
456 [SOCKET_CONNECTING] = true,
457 [SOCKET_CONNECTED] = true,
458 [SOCKET_CLOSED] = true,
459 },
461 {
462 [SOCKET_CONNECTED] = true,
463 [SOCKET_CLOSED] = true,
464 },
466 {
467 [SOCKET_CONNECTED] = true,
468 },
470 {
471 [SOCKET_CLOSING] = true,
472 },
474 {
475 [SOCKET_CLOSED] = true,
476 },
477 [SOCKET_CLOSED] = {},
478};
479
481{
482 if (from >= SOCKET_STATE_AMOUNT || to >= SOCKET_STATE_AMOUNT || from < 0 || to < 0)
483 {
484 return false;
485 }
486 return validTransitions[from][to];
487}
488
490{
491 if (sock == NULL)
492 {
493 errno = EINVAL;
494 return ERR;
495 }
496
498
499 if (!socket_can_transition(sock->currentState, state))
500 {
502 errno = EINVAL;
503 return ERR;
504 }
505
506 if (sock->currentState == state)
507 {
509 errno = EINVAL;
510 return ERR;
511 }
512
513 sock->nextState = state;
514 return 0;
515}
516
518{
519 sock->currentState = sock->nextState;
520
521 assert(socket_can_transition(sock->currentState, state)); // This should always pass.
522
523 sock->nextState = state;
524}
525
527{
528 if (result != ERR)
529 {
530 sock->currentState = sock->nextState;
531 }
532
533 sock->nextState = sock->currentState;
534
536}
#define assert(expression)
Definition assert.h:29
#define CTL_STANDARD_OPS_DEFINE(name,...)
Helper macro to define a standard ctl file operations structure.
Definition ctl.h:49
void path_put(path_t *path)
Put a path.
Definition path.c:246
#define PATH_EMPTY
Helper to create an empty path.
Definition path.h:194
@ MODE_ALL_PERMS
Definition path.h:87
@ MODE_NONBLOCK
Definition path.h:79
@ MODE_DIRECTORY
Definition path.h:84
void rwmutex_write_acquire(rwmutex_t *mtx)
Acquires a rwmutex for writing, blocking until it is available.
Definition rwmutex.c:85
void rwmutex_init(rwmutex_t *mtx)
Initializes a rwmutex.
Definition rwmutex.c:10
#define RWMUTEX_READ_SCOPE(mutex)
Acquires a rwmutex for reading for the reminder of the current scope.
Definition rwmutex.h:21
void rwmutex_deinit(rwmutex_t *mtx)
Deinitializes a rwmutex.
Definition rwmutex.c:20
void rwmutex_write_release(rwmutex_t *mtx)
Releases a rwmutex from writing.
Definition rwmutex.c:124
static void ref_init(ref_t *ref, void *free)
Initialize a reference counter.
Definition ref.h:92
#define REF(ptr)
Increment reference count.
Definition ref.h:65
#define UNREF(ptr)
Decrement reference count.
Definition ref.h:80
#define EINVAL
Invalid argument.
Definition errno.h:142
#define ENOSYS
Function not implemented.
Definition errno.h:222
#define ENOMEM
Out of memory.
Definition errno.h:92
#define EINPROGRESS
Operation now in progress.
Definition errno.h:602
#define errno
Error number variable.
Definition errno.h:27
#define ENOTCONN
Transport endpoint is not connected.
Definition errno.h:562
poll_events_t
Poll events type.
Definition io.h:257
@ MOUNT_PROPAGATE_CHILDREN
Propagate the mount to child namespaces.
Definition io.h:491
#define NULL
Pointer error value.
Definition NULL.h:23
#define ERR
Integer error value.
Definition ERR.h:17
uint64_t socket_family_get_dir(socket_family_t *family, path_t *outPath)
Get the directory of a socket family.
socket_type_t
Socket type enumeration.
Definition socket_type.h:18
socket_state_t
Socket states.
Definition socket.h:66
socket_t * socket_new(socket_family_t *family, socket_type_t type)
Create a new socket.
Definition socket.c:357
void socket_continue_transition(socket_t *sock, socket_state_t state)
Without releasing the socket mutex, start a transition to a new target state.
Definition socket.c:517
uint64_t socket_start_transition(socket_t *sock, socket_state_t state)
Starts a socket state transition.
Definition socket.c:489
void socket_end_transition(socket_t *sock, uint64_t result)
Ends a socket state transition.
Definition socket.c:526
@ SOCKET_CLOSED
Definition socket.h:73
@ SOCKET_LISTENING
Definition socket.h:69
@ SOCKET_STATE_AMOUNT
Definition socket.h:74
@ SOCKET_CONNECTED
Definition socket.h:71
@ SOCKET_NEW
Definition socket.h:67
@ SOCKET_CLOSING
Definition socket.h:72
@ SOCKET_BOUND
Definition socket.h:68
@ SOCKET_CONNECTING
Definition socket.h:70
static dentry_t * file
Definition log_file.c:22
static atomic_long count
Definition main.c:10
static mount_t * mount
Definition ramfs.c:28
static inode_ops_t inodeOps
Definition socket.c:311
static file_ops_t acceptOps
Definition socket.c:292
bool socket_can_transition(socket_state_t from, socket_state_t to)
Definition socket.c:480
static const bool validTransitions[SOCKET_STATE_AMOUNT][SOCKET_STATE_AMOUNT]
Definition socket.c:446
static void socket_unmount(superblock_t *superblock)
Definition socket.c:334
static uint64_t socket_accept_open(file_t *file)
Definition socket.c:233
static uint64_t socket_ctl_bind(file_t *file, uint64_t argc, const char **argv)
Definition socket.c:122
static void socket_free(socket_t *sock)
Definition socket.c:318
static wait_queue_t * socket_data_poll(file_t *file, poll_events_t *revents)
Definition socket.c:96
static superblock_ops_t superblockOps
Definition socket.c:353
static file_ops_t dataOps
Definition socket.c:114
static uint64_t socket_ctl_connect(file_t *file, uint64_t argc, const char **argv)
Definition socket.c:180
static void socket_accept_close(file_t *file)
Definition socket.c:281
static uint64_t socket_data_write(file_t *file, const void *buf, size_t count, uint64_t *offset)
Definition socket.c:70
static void socket_inode_cleanup(inode_t *inode)
Definition socket.c:300
static uint64_t socket_data_read(file_t *file, void *buf, size_t count, uint64_t *offset)
Definition socket.c:44
static void socket_data_close(file_t *file)
Definition socket.c:33
static uint64_t socket_data_open(file_t *file)
Definition socket.c:20
static uint64_t socket_ctl_listen(file_t *file, uint64_t argc, const char **argv)
Definition socket.c:149
#define atomic_fetch_add(object, operand)
Definition stdatomic.h:283
__UINT32_TYPE__ uint32_t
Definition stdint.h:15
__UINT64_TYPE__ uint64_t
Definition stdint.h:17
_PUBLIC int snprintf(char *_RESTRICT s, size_t n, const char *_RESTRICT format,...)
Definition snprintf.c:3
#define atol(nptr)
Definition stdlib.h:33
_PUBLIC void * calloc(size_t nmemb, size_t size)
Definition calloc.c:6
_PUBLIC void free(void *ptr)
Definition free.c:11
inode_t * inode
Will be NULL if the dentry is negative, once positive it will never be NULL.
Definition dentry.h:88
void * private
Definition dentry.h:95
File operations structure.
Definition file.h:54
uint64_t(* open)(file_t *file)
Definition file.h:55
File structure.
Definition file.h:39
Inode operations structure.
Definition inode.h:82
void(* cleanup)(inode_t *inode)
Cleanup function called when the inode is being freed.
Definition inode.h:134
Inode structure.
Definition inode.h:56
void * private
Definition inode.h:68
Mount structure.
Definition mount.h:44
superblock_t * superblock
The superblock of the mounted filesystem.
Definition mount.h:50
dentry_t * source
The dentry to appear at target once mounted, usually the root dentry of the mounted filesystem.
Definition mount.h:48
Path structure.
Definition path.h:125
uint64_t(* bind)(socket_t *sock, const char *address)
wait_queue_t *(* poll)(socket_t *sock, poll_events_t *revents)
uint64_t(* connect)(socket_t *sock, const char *address)
uint64_t(* listen)(socket_t *sock, uint32_t backlog)
uint64_t(* init)(socket_t *sock)
void(* deinit)(socket_t *sock)
uint64_t(* accept)(socket_t *sock, socket_t *newSock, mode_t mode)
uint64_t(* recv)(socket_t *sock, void *buffer, uint64_t count, uint64_t *offset, mode_t mode)
uint64_t(* send)(socket_t *sock, const void *buffer, uint64_t count, uint64_t *offset, mode_t mode)
Socket Family structure.
const socket_family_ops_t * ops
atomic_uint64_t newId
Socket structure.
Definition socket.h:82
dentry_t * dataFile
Definition socket.h:93
socket_state_t nextState
Definition socket.h:90
void * private
Definition socket.h:88
dentry_t * ctlFile
Definition socket.h:92
ref_t ref
Definition socket.h:83
socket_type_t type
Definition socket.h:87
socket_state_t currentState
Definition socket.h:89
socket_family_t * family
Definition socket.h:86
char id[MAX_NAME]
Definition socket.h:84
dentry_t * acceptFile
Definition socket.h:94
rwmutex_t mutex
Definition socket.h:91
Superblock operations structure.
Definition superblock.h:70
void(* unmount)(superblock_t *superblock)
Definition superblock.h:89
Superblock structure.
Definition superblock.h:44
void * private
Definition superblock.h:50
The primitive that threads block on.
Definition wait.h:182
mount_t * sysfs_mount_new(const path_t *parent, const char *name, namespace_t *ns, mount_flags_t flags, mode_t mode, const superblock_ops_t *superblockOps)
Mount a new instance of SysFS.
Definition sysfs.c:104
dentry_t * sysfs_file_new(dentry_t *parent, const char *name, const inode_ops_t *inodeOps, const file_ops_t *fileOps, void *private)
Create a new file inside a mounted SysFS instance.
Definition sysfs.c:216