{"id":20285630,"url":"https://github.com/aep/afunix_polyfill","last_synced_at":"2025-04-11T08:40:23.619Z","repository":{"id":140901997,"uuid":"61721657","full_name":"aep/afunix_polyfill","owner":"aep","description":"AF_UNIX with SOCK_DGRAM broadcast fixed in userspace for a complete IPC solution","archived":false,"fork":false,"pushed_at":"2016-07-26T11:46:43.000Z","size":32,"stargazers_count":6,"open_issues_count":0,"forks_count":1,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-10T17:54:56.490Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aep.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2016-06-22T13:34:25.000Z","updated_at":"2024-11-12T10:35:29.000Z","dependencies_parsed_at":"2023-10-20T16:22:22.816Z","dependency_job_id":null,"html_url":"https://github.com/aep/afunix_polyfill","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aep%2Fafunix_polyfill","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aep%2Fafunix_polyfill/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aep%2Fafunix_polyfill/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aep%2Fafunix_polyfill/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aep","download_url":"https://codeload.github.com/aep/afunix_polyfill/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248362342,"owners_count":21091098,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-14T14:27:57.659Z","updated_at":"2025-04-11T08:40:23.596Z","avatar_url":"https://github.com/aep.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Build Status](https://travis-ci.org/aep/afunix_polyfill.svg?branch=master)](https://travis-ci.org/aep/afunix_polyfill)\n[![NoDependencies](http://aep.github.io/images/no-dependencies.svg)](#)\n\nafunix-polyfill - AF_UNIX fixed in userspace\n============================\n\nafunix-polyfill is a complete linux IPC solution, compared to dbus it is:\n\n- just a socket\n- serverless\n- single header file\n- data format agnostic\n- dependency-free\n- using unix permissions instead of custom policies\n- theoretically posix compliant\n- bullshit and NIH free\n\nA word of warning: this is an experiment or proof-of-concept or RFC. If you need something usable, check back 2017.\n\nThe backstory originates from ubus (aep-ubus, not openwrt-ubus),\nwhich supports aproximatly the same messaging directives as dbus without a server.\n\nBut when you think really hard,  \nalmost every IPC problem people have in linux can be reduced to:\n\n- send message to a group identified by name\n- optionally respond to that message\n\nwhich could easily be implemented with DGRAM on AF_UNIX plus SO_REUSEPORT and maybe SO_BROADCAST.  \nbut none of these things work in linux.  \nSo here is a 'polyfill' for that, which emulates it, until it gets fixed in linux.    \nAs soon as it's fixed, you can seemlessly transition to the proper posix api, by just removing the afunix_ prefix.\n\nUse Case Examples in plain posix\n-------------------------------------------\n\nhere's some pseudo code of how to implement any of the dbus\nfunctionality in just plain posix.  \nAt least you could, if linux didn't suck.\n\n1) \"remote procedure call returning void\"\n\n```C\n//server\nint sock = socket(AF_UNIX, SOCK_DGRAM, 0);\nbind(sock, \"/var/bus/myservice/dostuff\");\nfor(;;) { char buf[]; recv(socket, buf); printf(\"let's do stuff\"); }\n\n//client\nint sock = socket(AF_UNIX, SOCK_DGRAM, 0);\nconnect(sock, \"/var/bus/myservice/dostuff\");\nsend(sock, \"do it!\");\n```\n\n2) \"publish/subscribe\" or \"notification\"\n\n```C\n//server\nint sock = socket(AF_UNIX, SOCK_DGRAM, 0);\nbind(sock, \"/var/bus/myservice/event\");\nsendto(sock, \"hey everyone, things happened!\", BROADCAST);\n\n//client\nint sock = socket(AF_UNIX, SOCK_DGRAM, 0);\nconnect(sock, \"/var/bus/myservice/event\");\nrecv(socket, buf);\n```\n\n3) \"remote procedure call returning string\"\n\n```C\n//server\nint sock = socket(AF_UNIX, SOCK_DGRAM, 0);\nbind(sock, \"/var/bus/myservice/dostuff\");\nfor(;;) {\n    char buf[];\n    address sender;\n    recv_from(socket, buf, \u0026sender);\n    send_to(socket, \"ok i did it!\", sender);\n\n//client\nint sock = socket(AF_UNIX, SOCK_DGRAM, 0);\nconnect(sock, \"/var/bus/myservice/dostuff\");\nsend(sock, \"do it!\");\nrecv(socket, buf);\n```\n\nAnd poof, the whole thing is solved. The best part, this is already how AF_INET behaves!  \nAll it takes is copying the exact behaviour over to AF_UNIX.\n\nBut while we wait for hell to freeze over, let's hop over to using afunix_polyfill\n\nApi Design\n-----------\n\nshould be pretty self explaining,\nsince it just does exactly what the posix api is supposed to do.\n\n```C\n// creates all the polyfill backends and wraps them in a single fd,\n// which behaves exactly like a socket\nint fd = afunix_socket();\n\n// creates the file, and listens on it\n// valid options are:\n//  - SO_REUSEPORT round robin messages between all active binds\n//                 without that option, you can only have one bind\nint ret = afunix_bind(int fd, const char *name, int options);\n\n//receive a datagram\nchar     buf[128];\nuint32_t address\nafunix_recvfrom(fd, \u0026buf, AFUNIX_MAX_PACKAGE_SIZE, 0, \u0026address);\n\n//respond\nafunix_sendto(fd, \u0026buf, AFUNIX_MAX_PACKAGE_SIZE, 0, address);\n\n//broadcast\nafunix_sendto(fd, \u0026buf, AFUNIX_MAX_PACKAGE_SIZE, 0, 0);\n\n//connects to a file\nint ret = afunix_connect(int fd, const char *name, int options);\n\n//send to connected service\nsend(fd, \u0026buf, AFUNIX_MAX_PACKAGE_SIZE, 0);\nwrite(fd, \u0026buf, AFUNIX_MAX_PACKAGE_SIZE, 0);\n\n//receive response\nrecv(fd, \u0026buf, AFUNIX_MAX_PACKAGE_SIZE, 0);\n\n//close and clean up the polyfill backend\nafunix_close(fd);\n```\n\nSecurity\n--------\n\nInstead of handling a policy of \"who may open which service\" in some framework,\nyou can simply rely on file permissions.\n\n```bash\nchmod 600 /var/0chan/\nmkdir /var/0chan/cupsd/\nchown cups /var/0chan/cupsd/\n```\n\nexactly the same for \"who may talk to this service\"\n\n```bash\nchgrp cups /var/0chan/cupsd/something.cmd\n```\n\nUsing\n--------------\n\neverything is in a single header file 'afunix_polyfill.h',\nprefferably add a git submodule to your project and just include that header.\n\nLimitations and Gotchas\n--------------\n\n- using any of the standard posix functions instead of the prefixed afunix ones\n  is undefined behaviour. there is no way to detect that either.\n- two threads doing afunix_recvfrom() on the same chan at the same time,\n  is undefined behaviour, because passing the address is thread unaware.\n- same for afunix_send()\n- mixing afunix_recvfrom and recv on the same socket is undefined behaviour\n- the whole thing isnt thread safe yet, but that can be done later.\n  its more in proof of concept state\n- unlike with real DGRAM, there is no way to transport a 0 bytes package.\n  to prevent inconsistent behaviour, sending 0 bytes will close the channel\n\n\nHigh level functionality\n-----------------------\n\nIn addition to just fixing af_unix, this polyfill has optional high level apis,\nas options to connect and bind:A\n\n- AFUNIX_PATH_CONVENTION\n  in addition to an absolute file path,\n  take a shorter service name that is matched to an absolute path\n  according to conventions described below\n- AFUNIX_MAKE_PATH\n  when using service names, create the real paths automatically\n\npath conventions\n----------------\n\nthere's a system bus and a session bus.\nsystem is in /var/run/unixbus/\u003cservice\u003e/\u003cmethod\u003e.seqpacket\nservice is in $XDG_RUNTIME_DIR/unixbus/\u003cservice\u003e/\u003cmethod\u003e.seqpacket\n\nshort service names are identified as \u003csystem|session\u003e:\u003cservice\u003e:\u003cmethod\u003e,\nfor example:\n\n```C\nafunix_bind(fd, \"service:xlock:lock\", AFUNIX_PATH_CONVENTION | AFUNIX_MAKE_PATH);\n```\nwill create $XDG_RUNTIME_DIR/unixbus/xlock/lock.seqpacket\n\n\ncommandline tool\n----------------\n\nThe 'unixbus' commandline tool is in cmd.c and built with 'make' by default.\nThe main use cases are covered from a cient perspective:\n\n1) \"remote procedure call returning void\"\n```bash\n$ unixbus invoke session:myservice:dostuff\n$\n```\n\n2) \"publish/subscribe\" or \"notification\"\n```bash\n$ unixbus listen session:myservice:thatevent\nhey everyone, things happened!\nhey everyone, things happened again!\nwow su much happen!\n```\n\n3) \"remote procedure call returning string\"\n```bash\n$ unixbus call session:myservice:do these things\ni did these things!\n$\n```\n\nThere's also sever commands, although they're a bit excotic:\n\n```bash\n$ unixbus listen session:myservice:do\nderp\nmerp\n```\nother shell:\n\n```bash\n$ unixbus call session:myservice:do derp\n$ unixbus call session:myservice:do merp\n```\n\nxargs is pretty fun\n\n```bash\n$ echo 'echo $1 | tr a e' \u003e /tmp/foo\n$ unixbus xargs session:myservice:echo sh /tmp/foo\n```\n\nother shell:\n```bash\n$ unbixbus call session:myservice:echo amazing\nemezing\n$\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faep%2Fafunix_polyfill","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faep%2Fafunix_polyfill","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faep%2Fafunix_polyfill/lists"}