├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .gitmodules ├── Doxyfile ├── GNUmakefile ├── LICENSE.txt ├── README.md ├── include └── poser │ ├── core.h │ ├── core │ ├── base64.h │ ├── certinfo.h │ ├── client.h │ ├── connection.h │ ├── daemon.h │ ├── dictionary.h │ ├── event.h │ ├── hash.h │ ├── hashtable.h │ ├── ipaddr.h │ ├── json.h │ ├── list.h │ ├── log.h │ ├── process.h │ ├── proto.h │ ├── queue.h │ ├── random.h │ ├── ratelimit.h │ ├── resolver.h │ ├── runopts.h │ ├── server.h │ ├── service.h │ ├── stringbuilder.h │ ├── threadpool.h │ ├── timer.h │ └── util.h │ └── decl.h └── src └── lib └── core ├── assert.h ├── base64.c ├── certinfo.c ├── certinfo.h ├── client.c ├── client.h ├── connection.c ├── connection.h ├── core.mk ├── daemon.c ├── dictionary.c ├── event.c ├── event.h ├── hash.c ├── hashtable.c ├── ipaddr.c ├── ipaddr.h ├── json.c ├── list.c ├── log.c ├── log.h ├── process.c ├── queue.c ├── random.c ├── ratelimit.c ├── resolver.c ├── runopts.c ├── runopts.h ├── server.c ├── service.c ├── service.h ├── sharedobj.c ├── sharedobj.h ├── stackmgr.c ├── stackmgr.h ├── stringbuilder.c ├── threadpool.c ├── threadpool.h ├── timer.c ├── timer.h ├── util.c ├── xxhash.c ├── xxhash.h └── xxhx86.c /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | jobs: 10 | build: 11 | strategy: 12 | matrix: 13 | atomics: [WITH_ATOMICS=on, WITH_ATOMICS=off] 14 | tls: [WITH_TLS=on, WITH_TLS=off] 15 | no_evfd: [WITHOUT_EVENTFD=off, WITHOUT_EVENTFD=on] 16 | no_epoll: [WITHOUT_EPOLL=off, WITHOUT_EPOLL=on, 17 | WITHOUT_EPOLL=on WITH_POLL=on] 18 | no_sigfd: [WITHOUT_SIGNALFD=off, WITHOUT_SIGNALFD=on] 19 | no_timfd: [WITHOUT_TIMERFD=off, WITHOUT_TIMERFD=on] 20 | runs-on: ubuntu-latest 21 | env: 22 | CI_CFLAGS: -O2 -std=c11 -Wall -Wextra -Wshadow -Werror -pedantic 23 | steps: 24 | - uses: actions/checkout@v3 25 | - name: submodules 26 | run: git submodule update --init 27 | - name: make 28 | run: >- 29 | make -j CFLAGS="${CI_CFLAGS}" 30 | ${{matrix.atomics}} 31 | ${{matrix.tls}} 32 | ${{matrix.no_evfd}} 33 | ${{matrix.no_epoll}} 34 | ${{matrix.no_sigfd}} 35 | ${{matrix.no_timfd}} 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .*.swp 2 | .*.cfg 3 | .*.pyc 4 | .path-cache 5 | *.o 6 | *.d 7 | *.exe 8 | *.core 9 | defaults.mk 10 | *.files 11 | *.config 12 | *.includes 13 | *.creator 14 | *.user 15 | *.bin 16 | *.exo 17 | *.prg 18 | /*.cfg 19 | /bin/ 20 | /lib/ 21 | /obj/ 22 | /test/ 23 | /dist/ 24 | /html/ 25 | html 26 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "zimk"] 2 | path = zimk 3 | url = https://github.com/Zirias/zimk.git 4 | [submodule "src/lib/core/contrib/xxHash"] 5 | path = src/lib/core/contrib/xxHash 6 | url = https://github.com/Cyan4973/xxHash.git 7 | -------------------------------------------------------------------------------- /GNUmakefile: -------------------------------------------------------------------------------- 1 | # WITH_ATOMICS If possible, use atomic operations to avoid locks 2 | # (default: on) 3 | # WITH_EVPORTS Force using event ports over select() even if not 4 | # detected (default: off) 5 | # WITH_EPOLL Force using epoll() over select() even if not detected 6 | # (default: off) 7 | # WITH_KQUEUE Force using kqueue() over select() even if not detected 8 | # (default: off) 9 | # WITH_POLL Prefer using poll() over select() (default: off) 10 | # WITH_TLS Build with TLS support (default: on) 11 | # WITHOUT_EVPORTS Disable using event ports over select() even if 12 | # available (default: off) 13 | # WITHOUT_EPOLL Disable using epoll() over select() even if available 14 | # (default: off) 15 | # WITHOUT_KQUEUE Disable using kqueue() over select() even if available 16 | # (default: off) 17 | # WITH_EVENTFD Force using eventfd() for waking up other worker 18 | # threads, even if not detected (default: off) 19 | # WITHOUT_EVENTFD Disable using eventfd() even if available 20 | # (default: off) 21 | # WITH_SIGNALFD Force using signalfd() for handling signals even if not 22 | # detected (default: off) 23 | # WITHOUT_SIGNALFD Disable using signalfd() even if available 24 | # (default: off) 25 | # WITH_TIMERFD Force using timerfd for timers even if not detected 26 | # (default: off) 27 | # WITHOUT_TIMERFD Disable using timerfd even if available 28 | # (default: off) 29 | # FD_SETSIZE Number of file descriptors usable with select(), for 30 | # systems allowing to configure that (default: 4096) 31 | # OPENSSLINC Path to OpenSSL include files, overriding pkgconfig 32 | # (default: empty) 33 | # OPENSSLLIB Path to OpenSSL libraries, overriding pkgconfig 34 | # (default: empty) 35 | 36 | BOOLCONFVARS_ON= WITH_ATOMICS WITH_TLS 37 | BOOLCONFVARS_OFF= WITH_EVENTFD WITH_EVPORTS WITH_EPOLL WITH_KQUEUE \ 38 | WITH_POLL WITH_SIGNALFD WITH_TIMERFD \ 39 | WITHOUT_EVENTFD WITHOUT_EVPORTS WITHOUT_EPOLL \ 40 | WITHOUT_KQUEUE WITHOUT_SIGNALFD WITHOUT_TIMERFD 41 | SINGLECONFVARS= FD_SETSIZE OPENSSLINC OPENSSLLIB 42 | 43 | DEFAULT_FD_SETSIZE= 4096 44 | 45 | USES= pkgconfig 46 | 47 | include zimk/zimk.mk 48 | 49 | $(call zinc, src/lib/core/core.mk) 50 | 51 | DOXYGEN?= doxygen 52 | 53 | docs: 54 | rm -fr html/* 55 | doxygen 56 | 57 | .PHONY: docs 58 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023, Felix Palmen 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # poser – a C framework for POsix SERvices 2 | 3 | This is a lightweight framework to easily implement a typical (networking) 4 | service in C, using an event-driven API. 5 | 6 | It only uses standard POSIX APIs, therefore it is not suitable for services 7 | that must scale to thousands of concurrent clients. 8 | 9 | Currently, only the `core` part is implemented. 10 | 11 | ## Dynamic library versioning 12 | 13 | Dynamic libraries in poser will follow a simple versioning scheme: 14 | 15 | * A full library version consists of `major.minor.revision`, e.g. 16 | `libposercore.so.1.0.0` has major version 1, minor version 0 and revision 0. 17 | * The major version is the *ABI* version. It will only be bumped when a change 18 | breaks the ABI (introduces changes that are not backwards-compatible). 19 | * The minor version is the *Feature* version. It will be bumped whenever new 20 | features are added in a backwards-compatible way. 21 | * The revision will be bumped when a change fixes some issue, not touching 22 | API/ABI at all. 23 | * The `SONAME` property of a dynamic library will only contain the major/ABI 24 | version (e.g. `libposercore.so.1`). 25 | 26 | So, updating a library with the same major/ABI version will never break 27 | consumers. Using consumers built against a newer minor/Feature version with an 28 | older version of a library *might* result in unresolved symbols. 29 | 30 | ## libposercore 31 | 32 | This is the core part offering basic functionality needed for every service: 33 | 34 | * `PSC_Event`: a simple event mechanism 35 | * `PSC_Log`: a simple logging abstraction 36 | * `PSC_Daemon`: generic code to automatically deamonize and correctly handle a 37 | pidfile 38 | * `PSC_Service`: a generic service main loop built around `pselect()` 39 | * `PSC_ThreadPool`: a thread pool used to offload jobs that might block, 40 | available to schedule own jobs on 41 | * `PSC_Connection`: an abstraction for a (socket) connection 42 | * `PSC_Server`: an abstraction for a server listening on a socket and 43 | accepting connections 44 | * Optional TLS support 45 | * A few utility functions and classes 46 | 47 | ### Quick start 48 | 49 | A typical structure for a service looks like this minimal example implementing 50 | a TCP server that just writes "Hello!" to every client: 51 | 52 | ```c 53 | #include 54 | #include 55 | 56 | static PSC_Server *server; 57 | 58 | static void sent(void *receiver, void *sender, void *args) { 59 | // Close the connection after it sent our message: 60 | PSC_Connection_close(sender, 0); 61 | } 62 | 63 | static void connected(void *receiver, void *sender, void *args) { 64 | // PSC_Server_clientConnected() gives us the new client connection in 65 | // the event args: 66 | PSC_Connection *client = args; 67 | 68 | // We want to know when data to the client was sent: 69 | PSC_Event_register(PSC_Connection_dataSent(client), 0, sent, 0); 70 | 71 | // Send "hello" to the client, also pass some id object, so we get a 72 | // "dataSent" event for it: 73 | PSC_Connection_sendTextAsync(client, "Hello!\n", client); 74 | } 75 | 76 | static void startup(void *receiver, void *sender, void *args) { 77 | // initialization, for example create a PSC_Server 78 | 79 | // Create TCP server options to listen on some port: 80 | PSC_TcpServerOpts *opts = PSC_TcpServerOpts_create(10000); 81 | 82 | // Create server using these options: 83 | server = PSC_Server_createTcp(opts); 84 | PSC_TcpServerOpts_destroy(opts); 85 | 86 | // In case of error, let PSC_Service know via event arguments: 87 | if (!server) { 88 | PSC_EAStartup_return(args, EXIT_FAILURE); 89 | return; 90 | } 91 | 92 | // We want to know when a client connects: 93 | PSC_Event_register(PSC_Server_clientConnected(server), 0, connected, 0); 94 | } 95 | 96 | static void shutdown(void *receiver, void *sender, void *args) { 97 | // cleanup, e.g. destroy objects created in startup() 98 | 99 | PSC_Server_destroy(server); 100 | } 101 | 102 | int main(void) { 103 | PSC_RunOpts_init("/tmp/example.pid"); // configure pidfile 104 | PSC_RunOpts_enableDefaultLogging("example"); // ident for syslog 105 | 106 | // Execute startup() early during service startup 107 | PSC_Event_register(PSC_Service_prestartup(), 0, startup, 0); 108 | 109 | // Execute shutdown() when service is about to stop 110 | PSC_Event_register(PSC_Service_shutdown(), 0, shutdown, 0); 111 | 112 | // Run the service 113 | return PSC_Service_run(); 114 | } 115 | ``` 116 | 117 | Reference documentation is available at https://zirias.github.io/poser/ and 118 | can be built from the source using `doxygen`. 119 | 120 | ### Threading model 121 | 122 | `libposercore` implements a service loop doing I/O multiplexing on a single 123 | thread. Different components are mostly "wired" using `PSC_Event`, which is 124 | not aware of threads and just calls any registered event handler directly. 125 | 126 | A thread pool is offered (and used internally as well) to offload either jobs 127 | that might block, or jobs that could be CPU intensive, because the main loop 128 | should always iterate quickly to serve all peers in a timely manner. 129 | 130 | For a thread job, you can pass some data which will be properly locked 131 | automatically at job start and end. But if you pass any "poser" objects there, 132 | be aware that executing any actions on them outside the main thread is **not** 133 | a safe thing to do! As a rule of thumb, treat them as read-only (`const`) 134 | inside your thread job. 135 | 136 | A notable exception is scheduling another thread job, this **is** a safe thing 137 | to do from any thread. 138 | 139 | Whether logging is safe depends on the log writer you use. The standard log 140 | writers offered either log to a file (which *should* be thread-safe on almost 141 | any platform) or to syslog (which is thread-safe). When logging is set to 142 | asynchronous mode (which is recommended to do after daemonizing and handled 143 | automatically when you use `PSC_RunOpts_enableDefaultLogging()`), any log 144 | writer is always called on some worker thread, so if you use your custom log 145 | writer, make sure it is thread-safe! 146 | 147 | -------------------------------------------------------------------------------- /include/poser/core.h: -------------------------------------------------------------------------------- 1 | #ifndef POSER_CORE_H 2 | #define POSER_CORE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /include/poser/core/base64.h: -------------------------------------------------------------------------------- 1 | #ifndef POSER_CORE_BAS64_H 2 | #define POSER_CORE_BAS64_H 3 | 4 | /** declarations for the PSC_Base64 class 5 | * @file 6 | */ 7 | 8 | #include 9 | 10 | #include 11 | 12 | /** Flags for Base64 encoding */ 13 | typedef enum PSC_Base64Flags 14 | { 15 | PSC_B64_DEFAULT = 0, /**< standard encoding */ 16 | PSC_B64_URLSAFE = (1 << 0) /**< use URL-safe alphabet */ 17 | } PSC_Base64Flags; 18 | 19 | /** Encode and decode data using Base64. 20 | * Provides static methods to encode and decode binary data with Base64. 21 | * @class PSC_Base64 base64.h 22 | */ 23 | 24 | /** Calculate Base64-encoded size. 25 | * @memberof PSC_Base64 26 | * @static 27 | * @param size the size of some binary data 28 | * @returns the length of the Base64-encoded string 29 | */ 30 | DECLEXPORT size_t 31 | PSC_Base64_encodedLen(size_t size) 32 | ATTR_CONST; 33 | 34 | /** Calculate Base64-decoded size. 35 | * @memberof PSC_Base64 36 | * @static 37 | * @param len the length of a Base64-encoded string 38 | * @returns the size of the decoded binary data 39 | */ 40 | DECLEXPORT size_t 41 | PSC_Base64_decodedSize(size_t len) 42 | ATTR_CONST; 43 | 44 | /** Base64-encode data to a given buffer. 45 | * @memberof PSC_Base64 46 | * @static 47 | * @param enc buffer to write Base64-encoded string to, must have enough 48 | * room for the encoded string length plus a NUL terminator 49 | * @param data the binary data to encode 50 | * @param size the size of the binary data 51 | * @param flags flags for encoding 52 | */ 53 | DECLEXPORT void 54 | PSC_Base64_encodeTo(char *enc, const void *data, size_t size, 55 | PSC_Base64Flags flags) 56 | ATTR_NONNULL((1)) ATTR_NONNULL((2)); 57 | 58 | /** Base64-encode data to a newly created string. 59 | * @memberof PSC_Base64 60 | * @static 61 | * @param data the binary data to encode 62 | * @param size the size of the binary data 63 | * @param flags flags for encoding 64 | * @returns a newly allocated Base64-encoded string 65 | */ 66 | DECLEXPORT char * 67 | PSC_Base64_encode(const void *data, size_t size, PSC_Base64Flags flags) 68 | ATTR_MALLOC ATTR_NONNULL((1)); 69 | 70 | /** Base64-decode a string to a given buffer. 71 | * @memberof PSC_Base64 72 | * @static 73 | * @param data buffer to write the decoded data to, must have enough room 74 | * for the decoded size 75 | * @param enc the Base64-encoded string 76 | * @param len the length of the Base64-encoded string 77 | */ 78 | DECLEXPORT void 79 | PSC_Base64_decodeTo(void *data, const char *enc, size_t len) 80 | ATTR_NONNULL((1)) ATTR_NONNULL((2)); 81 | 82 | /** Base64-decode a string to a newly allocated buffer. 83 | * @memberof PSC_Base64 84 | * @static 85 | * @param enc the Base64-encoded string 86 | * @param size if not NULL, set this to the size of the decoded data 87 | * @returns a newly allocated buffer containing the decoded data 88 | */ 89 | DECLEXPORT void * 90 | PSC_Base64_decode(const char *enc, size_t *size) 91 | ATTR_MALLOC ATTR_NONNULL((1)); 92 | 93 | #endif 94 | -------------------------------------------------------------------------------- /include/poser/core/certinfo.h: -------------------------------------------------------------------------------- 1 | #ifndef POSER_CORE_CERTINFO_H 2 | #define POSER_CORE_CERTINFO_H 3 | 4 | /** declarations for the PSC_CertInfo class 5 | * @file 6 | */ 7 | #include 8 | #include 9 | 10 | /** Metadata of an X.509 certificate. 11 | * This class holds some information about a certificate, used for custom 12 | * validation logic. 13 | * @class PSC_CertInfo certinfo.h 14 | */ 15 | C_CLASS_DECL(PSC_CertInfo); 16 | 17 | /** Custom certificate validator. 18 | * This can be used for custom logic whether a specific certificate should 19 | * be accepted or not. 20 | * @param receiver the object handling the validation 21 | * @param info metadata of the certificate to check 22 | * @returns 1 if acceptable, 0 if not 23 | */ 24 | typedef int (*PSC_CertValidator)(void *receiver, const PSC_CertInfo *info); 25 | 26 | /** Fingerprint of the certificate. 27 | * The fingerprint of the certificate in SHA512 format. The size will be 64 28 | * bytes. 29 | * @memberof PSC_CertInfo 30 | * @param self the PSC_CertInfo 31 | * @returns pointer to the fingerprint 32 | */ 33 | DECLEXPORT const uint8_t * 34 | PSC_CertInfo_fingerprint(const PSC_CertInfo *self) 35 | CMETHOD ATTR_RETNONNULL ATTR_PURE; 36 | 37 | /** Fingerprint string of the certificate. 38 | * The fingerprint of the certificate in SHA512 format as a hex str. 39 | * @memberof PSC_CertInfo 40 | * @param self the PSC_CertInfo 41 | * @returns the fingerprint as string 42 | */ 43 | DECLEXPORT const char * 44 | PSC_CertInfo_fingerprintStr(const PSC_CertInfo *self) 45 | CMETHOD ATTR_RETNONNULL ATTR_PURE; 46 | 47 | /** Subject name of the certificate. 48 | * The certificate's subject name. Warning: Don't use this for your custom 49 | * validation logic unless you also validate the issuing CA, otherwise it 50 | * would be inherently insecure! 51 | * @memberof PSC_CertInfo 52 | * @param self the PSC_CertInfo 53 | * @returns the subject name 54 | */ 55 | DECLEXPORT const char * 56 | PSC_CertInfo_subject(const PSC_CertInfo *self) 57 | CMETHOD ATTR_RETNONNULL ATTR_PURE; 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /include/poser/core/client.h: -------------------------------------------------------------------------------- 1 | #ifndef POSER_CORE_CLIENT_H 2 | #define POSER_CORE_CLIENT_H 3 | 4 | /** declarations for client construction of the PSC_Connection class 5 | * @file 6 | */ 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | C_CLASS_DECL(PSC_Connection); 14 | 15 | /** Options for creating a TCP client. 16 | * @class PSC_TcpClientOpts client.h 17 | */ 18 | C_CLASS_DECL(PSC_TcpClientOpts); 19 | 20 | /** Options for creating a UNIX socket client. 21 | * @class PSC_UnixClientOpts client.h 22 | */ 23 | C_CLASS_DECL(PSC_UnixClientOpts); 24 | 25 | /** Handler for completed async client creation. 26 | * This will be called once PSC_Connection_createTcpAsync created a 27 | * PSC_Connection object. 28 | * @param receiver the object receiving the callback 29 | * @param connection the newly created connection, or NULL when creation 30 | * failed 31 | */ 32 | typedef void (*PSC_ClientCreatedHandler)( 33 | void *receiver, PSC_Connection *connection); 34 | 35 | /** PSC_TcpClientOpts constructor. 36 | * Creates an options object initialized to default values. 37 | * @memberof PSC_TcpClientOpts 38 | * @param remotehost host to connect to (name or address) 39 | * @param port port to connect to 40 | * @returns a newly created options object 41 | */ 42 | DECLEXPORT PSC_TcpClientOpts * 43 | PSC_TcpClientOpts_create(const char *remotehost, int port) 44 | ATTR_RETNONNULL ATTR_NONNULL((1)); 45 | 46 | /** Set read buffer size. 47 | * Sets the size of the buffer used for reading from the connection, in bytes. 48 | * The default value is 16 kiB. 49 | * @memberof PSC_TcpClientOpts 50 | * @param self the PSC_TcpClientOpts 51 | * @param sz the size of the read buffer, must be > 0 52 | */ 53 | DECLEXPORT void 54 | PSC_TcpClientOpts_readBufSize(PSC_TcpClientOpts *self, size_t sz) 55 | CMETHOD; 56 | 57 | /** Enable TLS for the connection. 58 | * Enables TLS for the connection to be created, optionally using a client 59 | * certificate. 60 | * @memberof PSC_TcpClientOpts 61 | * @param self the PSC_TcpClientOpts 62 | * @param certfile certificate file for client certificate 63 | * @param keyfile private key file for client certificate 64 | */ 65 | DECLEXPORT void 66 | PSC_TcpClientOpts_enableTls(PSC_TcpClientOpts *self, 67 | const char *certfile, const char *keyfile) 68 | CMETHOD; 69 | 70 | /** Disable server certificate verification. 71 | * @memberof PSC_TcpClientOpts 72 | * @param self the PSC_TcpClientOpts 73 | */ 74 | DECLEXPORT void 75 | PSC_TcpClientOpts_disableCertVerify(PSC_TcpClientOpts *self) 76 | CMETHOD; 77 | 78 | /** Set a specific protocol (IPv4 or IPv6). 79 | * @memberof PSC_TcpClientOpts 80 | * @param self the PSC_TcpClientOpts 81 | * @param proto protocol the client should use 82 | */ 83 | DECLEXPORT void 84 | PSC_TcpClientOpts_setProto(PSC_TcpClientOpts *self, PSC_Proto proto) 85 | CMETHOD; 86 | 87 | /** Enable blacklisting of failed remote addresses. 88 | * When this is set to a non-zero value, a remote address is put on a 89 | * blacklist after errors or timeouts during connect or TLS hanshake, or when 90 | * closed with the blacklist parameter of PSC_Connection_close() set to 1. 91 | * 92 | * This can be useful for remote services using some load-balancing or 93 | * round-robin DNS. In this case, it can be avoided to try the same host over 94 | * and over again. 95 | * @memberof PSC_TcpClientOpts 96 | * @param self the PSC_TcpClientOpts 97 | * @param blacklistHits number of hits needed to remove the entry from the 98 | * blacklist again 99 | */ 100 | DECLEXPORT void 101 | PSC_TcpClientOpts_setBlacklistHits(PSC_TcpClientOpts *self, int blacklistHits) 102 | CMETHOD; 103 | 104 | /** PSC_TcpClientOpts destructor 105 | * @memberof PSC_TcpClientOpts 106 | * @param self the PSC_TcpClientOpts 107 | */ 108 | DECLEXPORT void 109 | PSC_TcpClientOpts_destroy(PSC_TcpClientOpts *self); 110 | 111 | /** PSC_UnixClientOpts constructor. 112 | * Creates an options object initialized to default values. 113 | * @memberof PSC_UnixClientOpts 114 | * @param sockname the name/path of the local socket to connect to 115 | * @returns a newly created options object 116 | */ 117 | DECLEXPORT PSC_UnixClientOpts * 118 | PSC_UnixClientOpts_create(const char *sockname) 119 | ATTR_RETNONNULL ATTR_NONNULL((1)); 120 | 121 | /** Set read buffer size. 122 | * Sets the size of the buffer used for reading from the connection, in bytes. 123 | * The default value is 16 kiB. 124 | * @memberof PSC_UnixClientOpts 125 | * @param self the PSC_UnixClientOpts 126 | * @param sz the size of the read buffer, must be > 0 127 | */ 128 | DECLEXPORT void 129 | PSC_UnixClientOpts_readBufSize(PSC_UnixClientOpts *self, size_t sz) 130 | CMETHOD; 131 | 132 | /** PSC_UnixClientOpts destructor 133 | * @memberof PSC_UnixClientOpts 134 | * @param self the PSC_UnixClientOpts 135 | */ 136 | DECLEXPORT void 137 | PSC_UnixClientOpts_destroy(PSC_UnixClientOpts *self); 138 | 139 | /** Create a connection as a TCP client. 140 | * The created connection will be in a "connecting" state. To know when it is 141 | * successfully connected, you must listen on the PSC_Connection_connected() 142 | * event. 143 | * @memberof PSC_Connection 144 | * @param opts TCP client options 145 | * @returns a newly created connection object, or NULL when creation failed 146 | */ 147 | DECLEXPORT PSC_Connection * 148 | PSC_Connection_createTcpClient(const PSC_TcpClientOpts *opts) 149 | ATTR_NONNULL((1)); 150 | 151 | /** Create a connection as a TCP client asynchronously. 152 | * To create a TCP client, it can be necessary to resolve a remote hostname, 153 | * and/or to read a client certificate to use with TLS. Using this function, 154 | * these things are done in background, so it is recommended to use this 155 | * function from within a running service. The connection is created when all 156 | * required information is present, and passed to the callback provided here. 157 | * 158 | * Note that the created connection will still be in a "connecting" state, so 159 | * you still have to listen on the PSC_Connection_connected() event to know 160 | * when it is successfully connected. 161 | * @memberof PSC_Connection 162 | * @param opts TCP client options 163 | * @param receiver the object to receive the callback (or NULL) 164 | * @param callback the callback function 165 | * @returns -1 on immediate error, 0 when in progress 166 | */ 167 | DECLEXPORT int 168 | PSC_Connection_createTcpClientAsync(const PSC_TcpClientOpts *opts, 169 | void *receiver, PSC_ClientCreatedHandler callback) 170 | ATTR_NONNULL((1)) ATTR_NONNULL((3)); 171 | 172 | /** Create a connection as a UNIX socket client. 173 | * The created connection will be in a "connecting" state. To know when it is 174 | * successfully connected, you must listen on the PSC_Connection_connected() 175 | * event. 176 | * @memberof PSC_Connection 177 | * @param opts UNIX client options 178 | * @returns a newly created connection object, or NULL when creation failed 179 | */ 180 | DECLEXPORT PSC_Connection * 181 | PSC_Connection_createUnixClient(const PSC_UnixClientOpts *opts) 182 | ATTR_NONNULL((1)); 183 | 184 | #endif 185 | -------------------------------------------------------------------------------- /include/poser/core/daemon.h: -------------------------------------------------------------------------------- 1 | #ifndef POSER_CORE_DAEMON_H 2 | #define POSER_CORE_DAEMON_H 3 | 4 | /** declarations for the PSC_Daemon class 5 | * @file 6 | */ 7 | 8 | #include 9 | 10 | /** Run something as a daemon. 11 | * @class PSC_Daemon daemon.h 12 | */ 13 | 14 | /** Main function for a daemon. 15 | * This is called by PSC_Daemon_run() in a detached child process. 16 | * @param data pointer to some custom data 17 | * @returns an exit code 18 | */ 19 | typedef int (*PSC_Daemon_main)(void *data); 20 | 21 | /** Run as a daemon. 22 | * Runs a given function in a detached child process and optionally handles a 23 | * pidfile. By default, it waits for successful startup of the daemon, passing 24 | * through stderr output of the child. The behavior can be configured using 25 | * PSC_Runopts. 26 | * 27 | * Note this is automatically used by PSC_Service_run(). 28 | * @memberof PSC_Daemon 29 | * @static 30 | * @param dmain the main function for the daemon 31 | * @param data data to pass to the main function 32 | * @returns an exit code 33 | */ 34 | DECLEXPORT int 35 | PSC_Daemon_run(PSC_Daemon_main dmain, void *data) 36 | ATTR_NONNULL((1)); 37 | 38 | /** Notify parent that the daemon successfully started. 39 | * When this is called from the running daemon, the parent process exits. 40 | * 41 | * Note this is automatically called by PSC_Service_run() if needed. 42 | * @memberof PSC_Daemon 43 | * @static 44 | */ 45 | DECLEXPORT void 46 | PSC_Daemon_launched(void); 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /include/poser/core/dictionary.h: -------------------------------------------------------------------------------- 1 | #ifndef POSER_CORE_DICTIONARY_H 2 | #define POSER_CORE_DICTIONARY_H 3 | 4 | /** Declarations for the PSC_Dictionary class 5 | * @file 6 | */ 7 | 8 | #include 9 | 10 | #include 11 | 12 | /** A dictionary storing any data objects using any type of keys. 13 | * Keys are interpreted as arrays of bytes of a specified size, so any object 14 | * stored in contiguous memory can be used as a key. For a string key, just 15 | * pass the string length as the size. 16 | * 17 | * This works internally as a structure of nested hash tables. A 64bit hash 18 | * function (currently xxHash 3) is used to calculate a hash of the given key. 19 | * The root of a PSC_Dictionary is a hash table with 256 entries, using the 20 | * lowest 8 bits of the hash. On collissions, a second level hash table with 21 | * another 256 entries is created, using the next 8 bits of the hash. 22 | * 23 | * Further collissions in a second-level table will create more child tables, 24 | * but only with 16 entries, using another 4 bits of the hash, which is 25 | * repeated until there are no bits of the hash left. If this is ever reached, 26 | * collissions are resolved using a linked list for the affected bucket of the 27 | * innermost hash table. 28 | * @class PSC_Dictionary dictionary.h 29 | */ 30 | C_CLASS_DECL(PSC_Dictionary); 31 | 32 | /** Dummy deleter, indicating entries should not be deleted. 33 | * @memberof PSC_Dictionary 34 | * @static 35 | */ 36 | DECLDATA void (*PSC_DICT_NODELETE)(void *); 37 | 38 | /** PSC_Dictionary default constructor. 39 | * Creates a new PSC_Dictionary 40 | * @memberof PSC_Dictionary 41 | * @param deleter Function to delete stored objects when they are removed, 42 | * replaced or the PSC_Dictionary is destroyed. If this is given 43 | * as NULL, individual deletion functions can be passed to 44 | * PSC_Dictionary_set(), otherwise functions passed there are 45 | * ignored. Can be set to the special value PSC_DICT_NODELETE 46 | * to completely disable deletion of stored objects for the 47 | * whole PSC_Dictionary. 48 | * @param shared If set non-zero, the dictionary is configured to be used from 49 | * multiple threads. 50 | * @returns a newly created PSC_Dictionary 51 | */ 52 | DECLEXPORT PSC_Dictionary * 53 | PSC_Dictionary_create(void (*deleter)(void *), int shared); 54 | 55 | /** Set a new object for a given key. 56 | * If the key already exists, the associated object is replaced. 57 | * @memberof PSC_Dictionary 58 | * @param self the PSC_Dictionary 59 | * @param key the key 60 | * @param keysz the size of the key 61 | * @param obj the new object to store, or NULL to remove the object for the 62 | * given key 63 | * @param deleter Individual deletion function for the stored object, ignored 64 | * if the dictionary was constructed with global deleter. 65 | */ 66 | DECLEXPORT void 67 | PSC_Dictionary_set(PSC_Dictionary *self, const void *key, size_t keysz, 68 | void *obj, void (*deleter)(void *)) 69 | CMETHOD ATTR_NONNULL((2)); 70 | 71 | /** Retrieve a stored object by its key. 72 | * @memberof PSC_Dictionary 73 | * @param self the PSC_Dictionary 74 | * @param key the key 75 | * @param keysz the size of the key 76 | * @returns the object stored for the given key, or NULL if not found 77 | */ 78 | DECLEXPORT void * 79 | PSC_Dictionary_get(const PSC_Dictionary *self, const void *key, size_t keysz) 80 | CMETHOD ATTR_PURE ATTR_NONNULL((2)); 81 | 82 | /** The total number of stored objects. 83 | * @memberof PSC_Dictionary 84 | * @param self the PSC_Dictionary 85 | * @returns the number of objects stored 86 | */ 87 | DECLEXPORT size_t 88 | PSC_Dictionary_count(const PSC_Dictionary *self) 89 | CMETHOD ATTR_PURE; 90 | 91 | /** Remove all matching entries from dictionary. 92 | * @memberof PSC_Dictionary 93 | * @param self the PSC_Dictionary 94 | * @param matcher function to check each entry, given by its key, the key 95 | * size and the stored object itself, according to some 96 | * specified argument. Must return 1 to have the entry removed, 97 | * 0 to leave it. 98 | * @param arg some argument for the matcher function 99 | * @returns the number of entries removed 100 | */ 101 | DECLEXPORT size_t 102 | PSC_Dictionary_removeAll(PSC_Dictionary *self, 103 | int (*matcher)(const void *, size_t, void *, const void *), 104 | const void *arg) 105 | CMETHOD ATTR_NONNULL((1)); 106 | 107 | /** PSC_Dictionary destructor. 108 | * Stored objects are also destructed if a global deleter is set or they have 109 | * individual deleters attached. 110 | * @memberof PSC_Dictionary 111 | * @param self the PSC_Dictionary 112 | */ 113 | void 114 | PSC_Dictionary_destroy(PSC_Dictionary *self); 115 | 116 | #endif 117 | -------------------------------------------------------------------------------- /include/poser/core/event.h: -------------------------------------------------------------------------------- 1 | #ifndef POSER_CORE_EVENT_H 2 | #define POSER_CORE_EVENT_H 3 | 4 | /** declarations for the PSC_Event class 5 | * @file 6 | */ 7 | 8 | #include 9 | 10 | /** An event handler. 11 | * @param receiver the object that subscribed to the event 12 | * @param sender the object that raised the event 13 | * @param args optional event arguments 14 | */ 15 | typedef void (*PSC_EventHandler)(void *receiver, void *sender, void *args); 16 | 17 | /** A simple event class. 18 | * Events can be raised and subscribed to. The class emitting an event should 19 | * own it and provide an accessor method for consumers to subscribe to it. 20 | * 21 | * When an event is raised, all subscribers are called directly. No mechanism 22 | * to cross threads is offered, so always use functions/objects wired by 23 | * events on the same thread. 24 | * @class PSC_Event event.h 25 | */ 26 | C_CLASS_DECL(PSC_Event); 27 | 28 | /** PSC_Event constructor. 29 | * @memberof PSC_Event 30 | * @param sender the object owning the event, or NULL for static classes 31 | * @returns a newly created event 32 | */ 33 | DECLEXPORT PSC_Event * 34 | PSC_Event_create(void *sender) 35 | ATTR_RETNONNULL; 36 | 37 | /** Register an event handler. 38 | * @memberof PSC_Event 39 | * @param self the PSC_Event 40 | * @param receiver the object that should receive the event 41 | * @param handler the handler to be called 42 | * @param id optional identifier to be matched, use 0 if not applicable 43 | */ 44 | DECLEXPORT void 45 | PSC_Event_register(PSC_Event *self, void *receiver, 46 | PSC_EventHandler handler, int id) 47 | CMETHOD ATTR_NONNULL((3)); 48 | 49 | /** Unregister an event handler. 50 | * When this is called with the exact same parameters as PSC_Event_register(), 51 | * it removes the event handler from the event again. 52 | * @memberof PSC_Event 53 | * @param self the PSC_Event 54 | * @param receiver the object that should receive the event 55 | * @param handler the handler to be called 56 | * @param id optional identifier to be matched, use 0 if not applicable 57 | */ 58 | DECLEXPORT void 59 | PSC_Event_unregister(PSC_Event *self, void *receiver, 60 | PSC_EventHandler handler, int id) 61 | CMETHOD ATTR_NONNULL((3)); 62 | 63 | /** Raise an event. 64 | * Called by the event owner to notify all subscribers that the event occured. 65 | * Only handlers with the same identifier will be called. 66 | * @memberof PSC_Event 67 | * @param self the PSC_Event 68 | * @param id optional identifier to be matched, use 0 if not applicable 69 | * @param args optional event args to pass to the handler(s) 70 | */ 71 | DECLEXPORT void 72 | PSC_Event_raise(PSC_Event *self, int id, void *args) 73 | CMETHOD; 74 | 75 | /** PSC_Event destructor. 76 | * @memberof PSC_Event 77 | * @param self the PSC_Event 78 | */ 79 | DECLEXPORT void 80 | PSC_Event_destroy(PSC_Event *self); 81 | 82 | #endif 83 | -------------------------------------------------------------------------------- /include/poser/core/hash.h: -------------------------------------------------------------------------------- 1 | #ifndef POSER_CORE_HASH_H 2 | #define POSER_CORE_HASH_H 3 | 4 | /** declarations for the PSC_Hash class 5 | * @file 6 | */ 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | /** Calculate non-cryptographic hashes of some data. 14 | * This class hashes some given data using xxhash3. 15 | * @class PSC_Hash hash.h 16 | */ 17 | C_CLASS_DECL(PSC_Hash); 18 | 19 | /** PSC_Hash default constructor. 20 | * Creates a new PSC_Hash with given options 21 | * @memberof PSC_Hash 22 | * @param func the hash function to use, currently ignored, only xxhash3 is 23 | * available 24 | * @param flags settings for the selected hash function, 0 means defaults. 25 | * For xxhash3, 1 means use a random secret (created only once 26 | * for all PSC_Hash instances) while hashing 27 | * @returns a newly created PSC_Hash, or NULL on error 28 | */ 29 | DECLEXPORT PSC_Hash * 30 | PSC_Hash_create(int func, int flags); 31 | 32 | /** Calculate a hash from any data. 33 | * @memberof PSC_Hash 34 | * @param self the PSC_Hash 35 | * @param data the data to hash 36 | * @param size the size of the data 37 | * @returns the hash value 38 | */ 39 | DECLEXPORT uint64_t 40 | PSC_Hash_bytes(PSC_Hash *self, const void *data, size_t size); 41 | 42 | /** Calculate a hash from a string. 43 | * @memberof PSC_Hash 44 | * @param self the PSC_Hash 45 | * @param str the string to hash 46 | * @returns the hash value 47 | */ 48 | DECLEXPORT uint64_t 49 | PSC_Hash_string(PSC_Hash *self, const char *str); 50 | 51 | /** PSC_Hash destructor. 52 | * @memberof PSC_Hash 53 | * @param self the PSC_Hash 54 | */ 55 | DECLEXPORT void 56 | PSC_Hash_destroy(PSC_Hash *self); 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /include/poser/core/hashtable.h: -------------------------------------------------------------------------------- 1 | #ifndef POSER_CORE_HASHTABLE_H 2 | #define POSER_CORE_HASHTABLE_H 3 | 4 | /** declarations for the PSC_HashTable class 5 | * @file 6 | */ 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | /** A hash table storing any data objects using string keys. 14 | * @class PSC_HashTable hashtable.h 15 | */ 16 | C_CLASS_DECL(PSC_HashTable); 17 | 18 | /** An iterator over the contents of a PSC_HashTable. 19 | * @class PSC_HashTableIterator hashtable.h 20 | */ 21 | C_CLASS_DECL(PSC_HashTableIterator); 22 | 23 | /** PSC_HashTable default constructor. 24 | * Creates a new PSC_HashTable 25 | * @memberof PSC_HashTable 26 | * @param bits number of bits for the hashes (valid range [2..16]) 27 | * @returns a newly created PSC_HashTable 28 | */ 29 | DECLEXPORT PSC_HashTable * 30 | PSC_HashTable_create(uint8_t bits) 31 | ATTR_RETNONNULL; 32 | 33 | /** Set a new object for a key. 34 | * If there was already an object for the given key, it is replaced. The old 35 | * object is destroyed if it has a deleter attached. 36 | * @memberof PSC_HashTable 37 | * @param self the PSC_HashTable 38 | * @param key the key 39 | * @param obj the new object 40 | * @param deleter optional function to destroy the object 41 | */ 42 | DECLEXPORT void 43 | PSC_HashTable_set(PSC_HashTable *self, const char *key, 44 | void *obj, void (*deleter)(void *)) 45 | CMETHOD ATTR_NONNULL((2)) ATTR_NONNULL((3)); 46 | 47 | /** Deletes the object with the specified key. 48 | * If the object has a deleter attached, it is also destroyed. 49 | * @memberof PSC_HashTable 50 | * @param self the PSC_HashTable 51 | * @param key the key 52 | * @returns 1 if an object was deleted, 0 otherwise 53 | */ 54 | DECLEXPORT int 55 | PSC_HashTable_delete(PSC_HashTable *self, const char *key) 56 | CMETHOD ATTR_NONNULL((2)); 57 | 58 | /** Deletes matching objects from the hashtable. 59 | * Objects that have a deleter attached are also destroyed. 60 | * @memberof PSC_HashTable 61 | * @param self the PSC_HashTable 62 | * @param matcher function to compare each entry, given by its key and the 63 | * stored object, to some specified value. Must return 1 for 64 | * entries to be deleted, 0 otherwise. 65 | * @param arg some value for the matcher function to compare entries against. 66 | * @returns the number of entries deleted 67 | */ 68 | DECLEXPORT int 69 | PSC_HashTable_deleteAll(PSC_HashTable *self, 70 | int (*matcher)(const char *, void *, const void *), const void *arg) 71 | CMETHOD ATTR_NONNULL((1)); 72 | 73 | /** Number of entries. 74 | * @memberof PSC_HashTable 75 | * @param self the PSC_HashTable 76 | * @returns the number of entries 77 | */ 78 | DECLEXPORT size_t 79 | PSC_HashTable_count(const PSC_HashTable *self) 80 | CMETHOD ATTR_PURE; 81 | 82 | /** Gets an object by key. 83 | * @memberof PSC_HashTable 84 | * @param self the PSC_HashTable 85 | * @param key the key 86 | * @returns the object stored for the give key, or NULL 87 | */ 88 | DECLEXPORT void * 89 | PSC_HashTable_get(const PSC_HashTable *self, const char *key) 90 | CMETHOD ATTR_NONNULL((2)); 91 | 92 | /** Creates an iterator for all entries. 93 | * The iterator contains a snapshot of all objects currently stored, 94 | * modifications to the PSC_HashTable will not be reflected in the iterator. 95 | * In its initial state, the iterator points to an invalid position. 96 | * @memberof PSC_HashTable 97 | * @param self the PSC_HashTable 98 | * @returns an iterator 99 | */ 100 | DECLEXPORT PSC_HashTableIterator * 101 | PSC_HashTable_iterator(const PSC_HashTable *self) 102 | CMETHOD ATTR_RETNONNULL; 103 | 104 | /** PSC_HashTable destructor. 105 | * All stored objects that have a deleter attached are destroyed as well. 106 | * @memberof PSC_HashTable 107 | * @param self the PSC_HashTable 108 | */ 109 | DECLEXPORT void 110 | PSC_HashTable_destroy(PSC_HashTable *self); 111 | 112 | /** Move to the next position. 113 | * If the position was invalid, move to the first position. If the position 114 | * was pointing to the last entry, move to invalid position. 115 | * @memberof PSC_HashTableIterator 116 | * @param self the PSC_HashTableIterator 117 | * @returns 1 if the new position is a valid one, 0 otherwise 118 | */ 119 | DECLEXPORT int 120 | PSC_HashTableIterator_moveNext(PSC_HashTableIterator *self) 121 | CMETHOD; 122 | 123 | /** Gets the key at the current position. 124 | * @memberof PSC_HashTableIterator 125 | * @param self the PSC_HashTableIterator 126 | * @returns the current key, or NULL for the invalid position 127 | */ 128 | DECLEXPORT const char * 129 | PSC_HashTableIterator_key(const PSC_HashTableIterator *self) 130 | CMETHOD ATTR_PURE; 131 | 132 | /** Gets the object at the current position. 133 | * @memberof PSC_HashTableIterator 134 | * @param self the PSC_HashTableIterator 135 | * @returns the current object, or NULL for the invalid position 136 | */ 137 | DECLEXPORT void * 138 | PSC_HashTableIterator_current(const PSC_HashTableIterator *self) 139 | CMETHOD ATTR_PURE; 140 | 141 | /** PSC_HashTableIterator destructor. 142 | * @memberof PSC_HashTableIterator 143 | * @param self the PSC_HashTableIterator 144 | */ 145 | DECLEXPORT void 146 | PSC_HashTableIterator_destroy(PSC_HashTableIterator *self); 147 | 148 | #endif 149 | -------------------------------------------------------------------------------- /include/poser/core/ipaddr.h: -------------------------------------------------------------------------------- 1 | #ifndef POSER_CORE_IPADDR_H 2 | #define POSER_CORE_IPADDR_H 3 | 4 | /** Declarations for the PSC_IpAddr class 5 | * @file 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | /** An IPv4 or IPv6 address or network. 12 | * This class holds data for an IP address or network with a prefix length 13 | * and offers functions to parse a string representation, create a string in 14 | * the canonical formats, convert between IPv4 and IPv6 and match networks. 15 | * @class PSC_IpAddr ipaddr.h 16 | */ 17 | C_CLASS_DECL(PSC_IpAddr); 18 | 19 | /** PSC_IpAddr default constructor. 20 | * Creates a new PSC_IpAddr from a given string. 21 | * @memberof PSC_IpAddr 22 | * @param str the address as a string 23 | * @returns a newly created PSC_IpAddr, or NULL if the string did not contain 24 | * a valid address. 25 | */ 26 | DECLEXPORT PSC_IpAddr * 27 | PSC_IpAddr_create(const char *str) 28 | ATTR_NONNULL((1)); 29 | 30 | /** Get another reference to a PSC_IpAddr. 31 | * Increments the internal reference counter by one. 32 | * @memberof PSC_IpAddr 33 | * @param self the PSC_IpAddr 34 | * @returns the PSC_IpAddr with incremented reference counter 35 | */ 36 | DECLEXPORT PSC_IpAddr * 37 | PSC_IpAddr_ref(const PSC_IpAddr *self) 38 | CMETHOD ATTR_RETNONNULL; 39 | 40 | /** Create IPv4 equivalent of a given IPv6 address. 41 | * The given address must be an IPv6 address with a prefix length of at least 42 | * 96, which is then matched against a given list of prefixes. These prefixes 43 | * must be IPv6 addresses with a prefix length of exactly 96. 44 | * 45 | * If one of them matches, a new IPv4 address is created from only the last 4 46 | * bytes and with 96 subtracted from the original prefix length. 47 | * 48 | * This can be used to map back IPv6 addresses obtained from NAT64. 49 | * @memberof PSC_IpAddr 50 | * @param self the PSC_IpAddr 51 | * @param prefixes an array of prefixes, must contain at least one entry and 52 | * must be terminated with NULL 53 | * @returns a newly created PSC_IpAddr holding the Ipv4 equivalent, or NULL 54 | */ 55 | DECLEXPORT PSC_IpAddr * 56 | PSC_IpAddr_tov4(const PSC_IpAddr *self, const PSC_IpAddr **prefixes) 57 | CMETHOD ATTR_NONNULL((2)); 58 | 59 | /** Create IPv6 equivalent of a given IPv4 address. 60 | * The given address must be an IPv4 address, the prefix must be an IPv6 61 | * address with a prefix length of exactly 96. Then, a new Ipv6 address is 62 | * returned, constructed from the first 12 bytes of the prefix, followed by 63 | * the original 4 bytes of the IPv4 address, and 96 added to the original 64 | * prefix length. 65 | * 66 | * This can be used to map IPv4 addresses for NAT64. 67 | * @memberof PSC_IpAddr 68 | * @param self the PSC_IpAddr 69 | * @param prefix the prefix to use for the mapping to IPv6 70 | * @returns a newly created PSC_IpAddr holding the IPv6 equivalent, or NULL 71 | */ 72 | DECLEXPORT PSC_IpAddr * 73 | PSC_IpAddr_tov6(const PSC_IpAddr *self, const PSC_IpAddr *prefix) 74 | CMETHOD ATTR_NONNULL((1)); 75 | 76 | /** The protocol of the address. 77 | * @memberof PSC_IpAddr 78 | * @param self the PSC_IpAddr 79 | * @returns the protocol of the address 80 | */ 81 | DECLEXPORT PSC_Proto 82 | PSC_IpAddr_proto(const PSC_IpAddr *self) 83 | CMETHOD ATTR_PURE; 84 | 85 | /** The prefix length of the address. 86 | * @memberof PSC_IpAddr 87 | * @param self the PSC_IpAddr 88 | * @returns the prefix length of the address 89 | */ 90 | DECLEXPORT unsigned 91 | PSC_IpAddr_prefixlen(const PSC_IpAddr *self) 92 | CMETHOD ATTR_PURE; 93 | 94 | /** The canonical string representation of the address. 95 | * Returns the canonical string representation of the address. If the prefix 96 | * length is less than the full length in bits (32 for IPv4, 128 for IPv6), 97 | * it is appended after a slash ('/'). 98 | * @memberof PSC_IpAddr 99 | * @param self the PSC_IpAddr 100 | * @returns the string representation of the address 101 | */ 102 | DECLEXPORT const char * 103 | PSC_IpAddr_string(const PSC_IpAddr *self) 104 | CMETHOD ATTR_RETNONNULL ATTR_PURE; 105 | 106 | /** Compare two addresses for equality. 107 | * @memberof PSC_IpAddr 108 | * @param self the PSC_IpAddr 109 | * @param other another PSC_IpAddr 110 | * @returns 1 if both addresses are equal (protocol, prefix length and data), 111 | * 0 otherwise. 112 | */ 113 | DECLEXPORT int 114 | PSC_IpAddr_equals(const PSC_IpAddr *self, const PSC_IpAddr *other) 115 | CMETHOD ATTR_NONNULL((2)) ATTR_PURE; 116 | 117 | /** Check whether an address is part of a network. 118 | * @memberof PSC_IpAddr 119 | * @param self the PSC_IpAddr 120 | * @param prefix a PSC_IpAddr describing a network 121 | * @returns 1 if the address is part of the given network (the prefix length 122 | * is at most the same and for the network's prefix length, the bits 123 | * of the address are the same), 0 otherwise 124 | */ 125 | DECLEXPORT int 126 | PSC_IpAddr_matches(const PSC_IpAddr *self, const PSC_IpAddr *prefix) 127 | CMETHOD ATTR_NONNULL((2)) ATTR_PURE; 128 | 129 | /** PSC_IpAddr destructor. 130 | * If the internal reference counter is greater than one, the object is not 131 | * immediately destroyed, instead the counter is decremented. 132 | * @memberof PSC_IpAddr 133 | * @param self the PSC_IpAddr 134 | */ 135 | DECLEXPORT void 136 | PSC_IpAddr_destroy(PSC_IpAddr *self); 137 | 138 | #endif 139 | -------------------------------------------------------------------------------- /include/poser/core/list.h: -------------------------------------------------------------------------------- 1 | #ifndef POSER_CORE_LIST_H 2 | #define POSER_CORE_LIST_H 3 | 4 | #include 5 | 6 | #include 7 | 8 | /** declarations for the PSC_List class 9 | * @file 10 | */ 11 | 12 | /** A list of objects. 13 | * @class PSC_List list.h 14 | */ 15 | C_CLASS_DECL(PSC_List); 16 | 17 | /** An iterator over the contents of a PSC_List. 18 | * @class PSC_ListIterator list.h 19 | */ 20 | C_CLASS_DECL(PSC_ListIterator); 21 | 22 | /** PSC_List default constructor. 23 | * Creates a new PSC_List 24 | * @memberof PSC_List 25 | * @returns a newly created PSC_List 26 | */ 27 | DECLEXPORT PSC_List * 28 | PSC_List_create(void) 29 | ATTR_RETNONNULL; 30 | 31 | /** Number of entries. 32 | * @memberof PSC_List 33 | * @param self the PSC_List 34 | * @returns the number of entries 35 | */ 36 | DECLEXPORT size_t 37 | PSC_List_size(const PSC_List *self) 38 | CMETHOD ATTR_PURE; 39 | 40 | /** Gets an object by position. 41 | * @memberof PSC_List 42 | * @param self the PSC_List 43 | * @param idx position of the object 44 | * @returns the object stored at that position, or NULL 45 | */ 46 | DECLEXPORT void * 47 | PSC_List_at(const PSC_List *self, size_t idx) 48 | CMETHOD ATTR_PURE; 49 | 50 | /** Append an object to the list. 51 | * @memberof PSC_List 52 | * @param self the PSC_List 53 | * @param obj the new object 54 | * @param deleter optional function to destroy the object 55 | */ 56 | DECLEXPORT void 57 | PSC_List_append(PSC_List *self, void *obj, void (*deleter)(void *)) 58 | CMETHOD ATTR_NONNULL((2)); 59 | 60 | /** Remove a given object from the list. 61 | * The object will *not* be automatically destroyed. 62 | * @memberof PSC_List 63 | * @param self the PSC_List 64 | * @param obj the object to remove 65 | */ 66 | DECLEXPORT void 67 | PSC_List_remove(PSC_List *self, void *obj) 68 | CMETHOD ATTR_NONNULL((2)); 69 | 70 | /** Remove matching objects from the list. 71 | * Objects that are removed will be destroyed if they have a deleter attached. 72 | * @memberof PSC_List 73 | * @param self the PSC_List 74 | * @param matcher function to compare each object to some specified value, must 75 | * return 1 to have that object removed, 0 otherwise 76 | * @param arg some value for the matcher function to compare the objects 77 | * against. 78 | */ 79 | DECLEXPORT void 80 | PSC_List_removeAll(PSC_List *self, int (*matcher)(void *, const void *), 81 | const void *arg) 82 | CMETHOD ATTR_NONNULL((2)); 83 | 84 | /** Creates an iterator for all entries. 85 | * The iterator contains a snapshot of all objects currently stored, 86 | * modifications to the PSC_List will not be reflected in the iterator. In its 87 | * initial state, the iterator points to an invalid position. 88 | * @memberof PSC_List 89 | * @param self the PSC_List 90 | * @returns an iterator 91 | */ 92 | DECLEXPORT PSC_ListIterator * 93 | PSC_List_iterator(const PSC_List *self) 94 | CMETHOD; 95 | 96 | /** Clear the list. 97 | * All stored objects are removed from the list, and those having a deleter 98 | * attached are also destroyed. 99 | * @memberof PSC_List 100 | * @param self the PSC_List 101 | */ 102 | DECLEXPORT void 103 | PSC_List_clear(PSC_List *self) 104 | CMETHOD; 105 | 106 | /** PSC_List destructor. 107 | * All stored objects that have a deleter attached are destroyed as well. 108 | * @memberof PSC_List 109 | * @param self the PSC_List 110 | */ 111 | DECLEXPORT void 112 | PSC_List_destroy(PSC_List *self); 113 | 114 | /** Move to the next position. 115 | * If the position was invalid, move to the first position. If the position was 116 | * pointing to the last entry, move to invalid position. 117 | * @memberof PSC_ListIterator 118 | * @param self the PSC_ListIterator 119 | * @returns 1 if the new position is a valid one, 0 otherwise 120 | */ 121 | DECLEXPORT int 122 | PSC_ListIterator_moveNext(PSC_ListIterator *self) 123 | CMETHOD; 124 | 125 | /** Gets the object at the current position. 126 | * @memberof PSC_ListIterator 127 | * @param self the PSC_ListIterator 128 | * @returns the current object, or NULL for the invalid position 129 | */ 130 | DECLEXPORT void * 131 | PSC_ListIterator_current(const PSC_ListIterator *self) 132 | CMETHOD ATTR_PURE; 133 | 134 | /** PSC_ListIterator destructor. 135 | * @memberof PSC_ListIterator 136 | * @param self the PSC_ListIterator 137 | */ 138 | DECLEXPORT void 139 | PSC_ListIterator_destroy(PSC_ListIterator *self); 140 | 141 | /** Create a List of strings by splitting a given string. 142 | * The string is split at any of the characters given in delim. Empty fields 143 | * are ignored. 144 | * @memberof PSC_List 145 | * @param str the string to split 146 | * @param delim characters that are considered delimiting fields 147 | * @returns a list of strings, or NULL if no non-empty fields were found 148 | */ 149 | DECLEXPORT PSC_List * 150 | PSC_List_fromString(const char *str, const char *delim); 151 | 152 | #endif 153 | -------------------------------------------------------------------------------- /include/poser/core/log.h: -------------------------------------------------------------------------------- 1 | #ifndef POSER_CORE_LOG_H 2 | #define POSER_CORE_LOG_H 3 | 4 | /** declarations for the PSC_Log class 5 | * @file 6 | */ 7 | 8 | #include 9 | 10 | #include 11 | 12 | /** A logging mechanism offering pluggable writers. 13 | * This is a simple logging mechanism that can use any write function to 14 | * implement different "log sinks". A function writing to an opened FILE and a 15 | * function writing to syslog are included. It can optionally log 16 | * asynchronously (making use of PSC_ThreadPool). 17 | * 18 | * By default, no log writer is configured, so any log messages are just 19 | * dropped. You have to set a log writer if you want to see log messages. 20 | * 21 | * Note that PSC_Service_Run() automatically configures log writers if 22 | * configured to do so via PSC_RunOpts. 23 | * @class PSC_Log log.h 24 | */ 25 | 26 | /** Maximum lenght for a log message. 27 | * This is the maximum length that can safely be used for a single log 28 | * message. 29 | */ 30 | #define PSC_MAXLOGLINE 16384 31 | 32 | /** Log levels. 33 | */ 34 | typedef enum PSC_LogLevel 35 | { 36 | PSC_L_FATAL, /**< fatal condition, service must abort */ 37 | PSC_L_ERROR, /**< error condition, something can't complete */ 38 | PSC_L_WARNING, /**< something seems wrong and should get attention */ 39 | PSC_L_INFO, /**< info logging to see what the service does */ 40 | PSC_L_DEBUG /**< verbose debugging messages */ 41 | } PSC_LogLevel; 42 | 43 | /** A log writer. 44 | * Called for any log message to actually write it somewhere. 45 | * @param level the log level 46 | * @param message the log message 47 | * @param data optional context data for the writer 48 | */ 49 | typedef void (*PSC_LogWriter)(PSC_LogLevel level, 50 | const char *message, void *data) 51 | ATTR_NONNULL((2)); 52 | 53 | /** Use a standard file logger. 54 | * @memberof PSC_Log 55 | * @static 56 | * @param file the opened file to write log messages to 57 | */ 58 | DECLEXPORT void 59 | PSC_Log_setFileLogger(FILE *file) 60 | ATTR_NONNULL((1)); 61 | 62 | /** Use a standard syslog loggeer. 63 | * @memberof PSC_Log 64 | * @static 65 | * @param ident the name to use for syslog (name of your service) 66 | * @param facility the syslog facility (e.g. LOG_DAEMON) 67 | * @param withStderr (0) or (1): also write to stderr 68 | */ 69 | DECLEXPORT void 70 | PSC_Log_setSyslogLogger(const char *ident, int facility, int withStderr) 71 | ATTR_NONNULL((1)); 72 | 73 | /** Use a custom log writer. 74 | * @memberof PSC_Log 75 | * @static 76 | * @param writer the log writer 77 | * @param data optional context data for the writer 78 | */ 79 | DECLEXPORT void 80 | PSC_Log_setCustomLogger(PSC_LogWriter writer, void *data) 81 | ATTR_NONNULL((1)); 82 | 83 | /** Set maximum log level. 84 | * Only log messages up to this level. Highest: PSC_L_DEBUG, lowest: 85 | * PSC_L_FATAL. Default is PSC_L_INFO. 86 | * @memberof PSC_Log 87 | * @static 88 | * @param level the new maximum log level 89 | */ 90 | DECLEXPORT void 91 | PSC_Log_setMaxLogLevel(PSC_LogLevel level); 92 | 93 | /** Enable/disable silent mode. 94 | * This can be used to temporarily suppress all messages except PSC_L_FATAL 95 | * and PSC_L_ERROR, regardless of the maximum log level. 96 | * @memberof PSC_Log 97 | * @static 98 | * @param silent 1: enable silent mode, 0: disable silent mode 99 | */ 100 | DECLEXPORT void 101 | PSC_Log_setSilent(int silent); 102 | 103 | /** Enable/disable asynchronous logging. 104 | * In asynchronous mode, calling the actual log writer is done on a worker 105 | * thread, to avoid having to wait for I/O caused by it on the main thread. 106 | * 107 | * Asynchronous mode is disabled by default, but it is recommended to enable 108 | * it once your service is up and running, as long as you can't guarantee your 109 | * log writer will never block. Note that both writing to a file (on disk) and 110 | * writing to syslog *can* block. 111 | * 112 | * Also note PSC_Service_run() automatically enables asynchronous logging when 113 | * configured to handle logging via PSC_RunOpts. 114 | * @memberof PSC_Log 115 | * @static 116 | * @param async 1: enable asynchronous mode, 0: disable asynchronous mode 117 | */ 118 | DECLEXPORT void 119 | PSC_Log_setAsync(int async); 120 | 121 | /** Check whether log level is enabled. 122 | * Checks whether a given log level would currently produce a log message. 123 | * @memberof PSC_Log 124 | * @static 125 | * @param level the log level to check 126 | * @returns 1 if the level is enabled, 0 otherwise 127 | */ 128 | DECLEXPORT int 129 | PSC_Log_enabled(PSC_LogLevel level); 130 | 131 | /** Log a message. 132 | * @memberof PSC_Log 133 | * @static 134 | * @param level the log level 135 | * @param message the message 136 | */ 137 | DECLEXPORT void 138 | PSC_Log_msg(PSC_LogLevel level, const char *message) 139 | ATTR_NONNULL((2)); 140 | 141 | /** Log a message using a printf-like format string. 142 | * @memberof PSC_Log 143 | * @static 144 | * @param level the log level 145 | * @param format the printf-like format string 146 | * @param ... optional arguments for conversions in the format string 147 | */ 148 | DECLEXPORT void 149 | PSC_Log_fmt(PSC_LogLevel level, const char *format, ...) 150 | ATTR_NONNULL((2)) ATTR_FORMAT((printf, 2, 3)); 151 | 152 | /** Log an error message based on 'errno' (like perror()) 153 | * @memberof PSC_Log 154 | * @static 155 | * @param level the log level 156 | * @param message the message to prepend to the error string, separated by 157 | * a colon 158 | */ 159 | DECLEXPORT void 160 | PSC_Log_err(PSC_LogLevel level, const char *message) 161 | ATTR_NONNULL((2)); 162 | 163 | /** Log an error message based on 'errno', with extra formatting 164 | * This combines PSC_Log_fmt() and PSC_Log_err(): The message is formatted 165 | * like printf() does, then the error string from the current 'errno' value 166 | * is appended like perror() does. 167 | * @memberof PSC_Log 168 | * @static 169 | * @param level the log level 170 | * @param format the printf-like format string 171 | * @param ... optional arguments for conversions in the format string 172 | */ 173 | DECLEXPORT void 174 | PSC_Log_errfmt(PSC_LogLevel level, const char *format, ...) 175 | ATTR_NONNULL((2)) ATTR_FORMAT((printf, 2, 3)); 176 | 177 | #endif 178 | 179 | -------------------------------------------------------------------------------- /include/poser/core/process.h: -------------------------------------------------------------------------------- 1 | #ifndef POSER_CORE_PROCESS_H 2 | #define POSER_CORE_PROCESS_H 3 | 4 | /** declarations for the PSC_Process class 5 | * @file 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | /** The default exit status used for exec() errors */ 12 | #define PSC_ERR_EXEC 127 13 | 14 | /** Standard I/O streams of the child process */ 15 | typedef enum PSC_StreamType 16 | { 17 | PSC_ST_STDIN, /**< standard input */ 18 | PSC_ST_STDOUT, /**< standard output */ 19 | PSC_ST_STDERR /**< standard error */ 20 | } PSC_StreamType; 21 | 22 | /** What to do with a standard I/O stream in a child process */ 23 | typedef enum PSC_StreamAction 24 | { 25 | PSC_SA_LEAVE, /**< leave it alone (inherit from parent) */ 26 | PSC_SA_CLOSE, /**< close it, so the child can't use it */ 27 | PSC_SA_NULL, /**< redirect it to /dev/null (silence) */ 28 | PSC_SA_PIPE /**< redirect it to a pipe */ 29 | } PSC_StreamAction; 30 | 31 | /** A child process. 32 | * This class offers creating and controlling a child process, optionally 33 | * opening pipes for the standard I/O streams that are represented by 34 | * instances of PSC_Connection. 35 | * 36 | * There is no explicit destructor. A PSC_Process object destroys itself 37 | * automatically when the child process exited and, if configured, the 38 | * pipes for standard output and standard error are closed. 39 | * 40 | * A hung process can be stopped with PSC_Process_stop(). 41 | * @class PSC_Process process.h 42 | */ 43 | C_CLASS_DECL(PSC_Process); 44 | 45 | /** Creation options for a child process. 46 | * @class PSC_ProcessOpts process.h 47 | */ 48 | C_CLASS_DECL(PSC_ProcessOpts); 49 | 50 | /** Event arguments for a finished child process. 51 | * @class PSC_EAProcessDone process.h 52 | */ 53 | C_CLASS_DECL(PSC_EAProcessDone); 54 | 55 | C_CLASS_DECL(PSC_Connection); 56 | C_CLASS_DECL(PSC_Event); 57 | 58 | /** A callback to receive a PSC_Connection for a standard I/O stream. 59 | * When redirection to a pipe is requested for one of the standard I/O 60 | * streams, this is called with the stream identifier and a PSC_Connection 61 | * object representing the parent's end of the pipe. 62 | * @param obj some object reference 63 | * @param type the standard I/O stream 64 | * @param conn the PSC_Connection to use 65 | */ 66 | typedef void (*PSC_StreamCallback)(void *obj, 67 | PSC_StreamType type, PSC_Connection *conn); 68 | 69 | /** A main function for a child process. 70 | * This is called in a child process by PSC_Process_run(). 71 | * @param argc the number of arguments, including the process name, which 72 | * will be NULL if not specified explicitly 73 | * @param argv the array of arguments 74 | * @returns an exit status [-128..127] 75 | */ 76 | typedef int (*PSC_ProcessMain)(int argc, char **argv); 77 | 78 | /** PSC_ProcessOpts default constructor. 79 | * Creates a new PSC_ProcessOpts 80 | * @memberof PSC_ProcessOpts 81 | * @returns a newly created PSC_ProcessOpts 82 | */ 83 | DECLEXPORT PSC_ProcessOpts * 84 | PSC_ProcessOpts_create(void) 85 | ATTR_RETNONNULL; 86 | 87 | /** Set the name of the child process. 88 | * If set, this is passed as argv[0], and also becomes the display name for 89 | * tools like ps(1) when PSC_Process_exec() is used. If not set, argv[0] 90 | * will be NULL for PSC_Process_run(), while PSC_Process_exec() will default 91 | * to the path executed. 92 | * @memberof PSC_ProcessOpts 93 | * @param self the PSC_ProcessOpts 94 | * @param name the process name 95 | */ 96 | DECLEXPORT void 97 | PSC_ProcessOpts_setName(PSC_ProcessOpts *self, const char *name) 98 | CMETHOD ATTR_NONNULL((2)); 99 | 100 | /** Add a command-line argument. 101 | * All arguments added here are passed in argv[] to the child process. 102 | * @memberof PSC_ProcessOpts 103 | * @param self the PSC_ProcessOpts 104 | * @param arg the argment to add 105 | */ 106 | DECLEXPORT void 107 | PSC_ProcessOpts_addArg(PSC_ProcessOpts *self, const char *arg) 108 | CMETHOD ATTR_NONNULL((2)); 109 | 110 | /** Configure an action for a standard I/O stream of the child process. 111 | * @memberof PSC_ProcessOpts 112 | * @param self the PSC_ProcessOpts 113 | * @param stream which stream to configure 114 | * @param action what to do with the stream 115 | * @returns 0 on success, -1 if invalid values were passed 116 | */ 117 | DECLEXPORT int 118 | PSC_ProcessOpts_streamAction(PSC_ProcessOpts *self, PSC_StreamType stream, 119 | PSC_StreamAction action) 120 | CMETHOD; 121 | 122 | /** Configure which exit status to use to indicate exec() errors. 123 | * If not set, the default value PSC_ERR_EXEC (127) is used, which is also 124 | * what most shells use. This should be a status code (in the range 125 | * [-128..127]) the program you intend to execute never uses. 127 is typically 126 | * a safe choice. 127 | * @memberof PSC_ProcessOpts 128 | * @param self the PSC_ProcessOpts 129 | * @param execError the status code to use for exec() errors 130 | * @returns 0 on success, -1 if an invalid status code was passed 131 | */ 132 | DECLEXPORT int 133 | PSC_ProcessOpts_setExecError(PSC_ProcessOpts *self, int execError) 134 | CMETHOD; 135 | 136 | /** PSC_ProcessOpts destructor. 137 | * @memberof PSC_ProcessOpts 138 | * @param self the PSC_ProcessOpts 139 | */ 140 | DECLEXPORT void 141 | PSC_ProcessOpts_destroy(PSC_ProcessOpts *self); 142 | 143 | /** PSC_Process default constructor. 144 | * Creates a new PSC_Process configured as given by the PSC_ProcessOpts 145 | * passed. 146 | * @memberof PSC_Process 147 | * @param opts the options for the process 148 | */ 149 | DECLEXPORT PSC_Process * 150 | PSC_Process_create(const PSC_ProcessOpts *opts) 151 | ATTR_RETNONNULL; 152 | 153 | /** Execute an external program in the child process. 154 | * @memberof PSC_Process 155 | * @param self the PSC_Process 156 | * @param obj some object reference for the callback @p cb 157 | * @param cb a callback to receive PSC_Connection objects for configured 158 | * pipes. This must be given if any pipes are configured. 159 | * @param path the path to the external program 160 | * @returns 0 on success, -1 on error 161 | */ 162 | DECLEXPORT void 163 | PSC_Process_exec(PSC_Process *self, void *obj, PSC_StreamCallback cb, 164 | const char *path) 165 | CMETHOD ATTR_NONNULL((3)); 166 | 167 | /** Execute a given function in the child process. 168 | * @memberof PSC_Process 169 | * @param self the PSC_Process 170 | * @param obj some object reference for the callback @p cb 171 | * @param cb a callback to receive PSC_Connection objects for configured 172 | * pipes. This must be given if any pipes are configured. 173 | * @param main the function to execute in the child 174 | * @returns 0 on success, -1 on error 175 | */ 176 | DECLEXPORT void 177 | PSC_Process_run(PSC_Process *self, void *obj, PSC_StreamCallback cb, 178 | PSC_ProcessMain main) 179 | CMETHOD ATTR_NONNULL((3)); 180 | 181 | /** Send signals to stop the child process. 182 | * This sends the child process a TERM signal, and optionally later a KILL 183 | * signal. Fails if the child process is not running or sending a KILL signal 184 | * is already scheduled. 185 | * @memberof PSC_Process 186 | * @param self the PSC_Process 187 | * @param forceMs if not 0, schedule sending a KILL signal in @p forceMs 188 | * milliseconds if the child didn't terminate until then 189 | * @returns 0 on success, -1 on error 190 | */ 191 | DECLEXPORT int 192 | PSC_Process_stop(PSC_Process *self, unsigned forceMs) 193 | CMETHOD; 194 | 195 | /** The child process finished. 196 | * This event fires when the child process terminated, and, if there are pipes 197 | * configured for standard output and/or standard error, these pipes were 198 | * closed, for example by reaching EOF. It passes a PSC_EAProcessDone instance 199 | * as the event argument. 200 | * @memberof PSC_Process 201 | * @param self the PSC_Process 202 | * @returns the done event 203 | */ 204 | DECLEXPORT PSC_Event * 205 | PSC_Process_done(PSC_Process *self) 206 | CMETHOD ATTR_RETNONNULL; 207 | 208 | /** The process id of the child process. 209 | * This fails if the child is not currently running. 210 | * @memberof PSC_Process 211 | * @param self the PSC_Process 212 | * @returns the process id, or -1 on error 213 | */ 214 | DECLEXPORT pid_t 215 | PSC_Process_pid(const PSC_Process *self) 216 | CMETHOD; 217 | 218 | /** The exit status of the child process. 219 | * @memberof PSC_EAProcessDone 220 | * @param self the PSC_EAProcessDone 221 | * @returns the child's exit status, or PSC_CHILD_SIGNALED if the child was 222 | * terminated by a signal 223 | */ 224 | DECLEXPORT int 225 | PSC_EAProcessDone_status(const PSC_EAProcessDone *self) 226 | CMETHOD; 227 | 228 | /** The signal that terminated the child process. 229 | * @memberof PSC_EAProcessDone 230 | * @param self the PSC_EAProcessDone 231 | * @returns the signal that terminated the child process, or 0 if it exited 232 | * normally 233 | */ 234 | DECLEXPORT int 235 | PSC_EAProcessDone_signal(const PSC_EAProcessDone *self) 236 | CMETHOD; 237 | 238 | #endif 239 | -------------------------------------------------------------------------------- /include/poser/core/proto.h: -------------------------------------------------------------------------------- 1 | #ifndef POSER_CORE_PROTO_H 2 | #define POSER_CORE_PROTO_H 3 | 4 | /** declaration of the PSC_Proto enum 5 | * @file 6 | */ 7 | 8 | /** Protocol to use for TCP connections */ 9 | typedef enum PSC_Proto 10 | { 11 | PSC_P_ANY, /**< use both IPv4 and IPv6 as available */ 12 | PSC_P_IPv4, /**< use only IPv4 */ 13 | PSC_P_IPv6 /**< use only IPv6 */ 14 | } PSC_Proto; 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /include/poser/core/queue.h: -------------------------------------------------------------------------------- 1 | #ifndef POSER_CORE_QUEUE_H 2 | #define POSER_CORE_QUEUE_H 3 | 4 | #include 5 | 6 | /** declarations for the PSC_Queue class 7 | * @file 8 | */ 9 | 10 | /** A simple queue of objects 11 | * @class PSC_Queue queue.h 12 | */ 13 | C_CLASS_DECL(PSC_Queue); 14 | 15 | /** PSC_Queue default constructor. 16 | * Creates a new PSC_Queue 17 | * @memberof PSC_Queue 18 | * @returns a newly created PSC_Queue 19 | */ 20 | DECLEXPORT PSC_Queue * 21 | PSC_Queue_create(void) 22 | ATTR_RETNONNULL; 23 | 24 | /** Enqueue an object. 25 | * @memberof PSC_Queue 26 | * @param self the PSC_Queue 27 | * @param obj the object to enqueue 28 | * @param deleter optional function to destroy the object 29 | */ 30 | DECLEXPORT void 31 | PSC_Queue_enqueue(PSC_Queue *self, void *obj, void (*deleter)(void *)) 32 | CMETHOD ATTR_NONNULL((2)); 33 | 34 | /** Dequeue the oldest object. 35 | * The object will *not* be destroyed, so it can be used by the caller. 36 | * @memberof PSC_Queue 37 | * @param self the PSC_Queue 38 | * @returns the dequeued object, or NULL if the PSC_Queue was empty 39 | */ 40 | DECLEXPORT void * 41 | PSC_Queue_dequeue(PSC_Queue *self) 42 | CMETHOD; 43 | 44 | /** PSC_Queue destructor. 45 | * All still queued objects that have a deleter attached are also destroyed. 46 | * @memberof PSC_Queue 47 | * @param self the PSC_Queue 48 | */ 49 | DECLEXPORT void 50 | PSC_Queue_destroy(PSC_Queue *self); 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /include/poser/core/random.h: -------------------------------------------------------------------------------- 1 | #ifndef POSER_CORE_RANDOM_H 2 | #define POSER_CORE_RANDOM_H 3 | 4 | /** declarations for the PSC_Random class 5 | * @file 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | /** Get some random data. 14 | * Provides static methods to obtain random bytes and random strings. 15 | * If available, arc4random() is used for that, which is expected to never 16 | * block, never fail and return cryptographically secure random data. 17 | * 18 | * If arc4random is not available, several other methods are tried. 19 | * 20 | * - If available, getrandom() is the first fallback, with flags matching 21 | * the flags specified in the call to PSC_Random. 22 | * - If this is unavailable or fails, reading from /dev/urandom or /dev/random 23 | * is tried if applicable given the flags specified. 24 | * - Finally, if cryptographically secure randomness is not specifically 25 | * requested, an internal xorshift-based PRNG is used. 26 | * 27 | * @class PSC_Random random.h 28 | */ 29 | 30 | /** Flags controlling how random bytes are obtained */ 31 | typedef enum PSC_RandomFlags 32 | { 33 | PSC_RF_ANY = 0, /**< Allow any method for random data */ 34 | PSC_RF_NONBLOCK = (1 << 0), /**< Don't use methods that might block */ 35 | PSC_RF_WLOGPSEUDO = (1 << 1), /**< Log a warning when using simple PRNG */ 36 | PSC_RF_ELOGPSEUDO = (1 << 2), /**< Log an error when using simple PRNG */ 37 | PSC_RF_SECURE = (1 << 3) /**< Never use a simple PRNG */ 38 | } PSC_RandomFlags; 39 | 40 | /** Fill a buffer with random bytes. 41 | * This never fails unless PSC_RF_SECURE is given and arc4random() is not 42 | * available. 43 | * @memberof PSC_Random 44 | * @static 45 | * @param buf the buffer to fill 46 | * @param count the number of random bytes put into @p buf 47 | * @param flags flags controlling methods used 48 | * @returns the number of bytes written, which will be less than @p count 49 | * on error 50 | */ 51 | DECLEXPORT size_t 52 | PSC_Random_bytes(void *buf, size_t count, PSC_RandomFlags flags) 53 | ATTR_NONNULL((1)) ATTR_ACCESS((write_only, 1, 2)); 54 | 55 | /** Fill a buffer with a random string. 56 | * The string is constructed by base64-encoding random bytes. This never fails 57 | * unless PSC_RF_SECURE is given and arc4random() is not available. 58 | * @memberof PSC_Random 59 | * @static 60 | * @param str the buffer to put the string into 61 | * @param size the length of the string, including a terminating NUL 62 | * @param flags flags controlling methods used 63 | * @param b64flags flags for Base64 encoding 64 | * @returns the string size (including a terminating NUL) actually written, 65 | * which will be less than @p size on error 66 | */ 67 | DECLEXPORT size_t 68 | PSC_Random_string(char *str, size_t size, 69 | PSC_RandomFlags flags, PSC_Base64Flags b64flags) 70 | ATTR_NONNULL((1)) ATTR_ACCESS((write_only, 1, 2)); 71 | 72 | /** Create a newly allocated random string. 73 | * The string is constructed by base64-encoding random bytes. Memory for the 74 | * new string is allocated, so the caller has to free() it later. This never 75 | * fails unless PSC_RF_SECURE is given and arc4random() is not available. 76 | * @memberof PSC_Random 77 | * @static 78 | * @param count use this many random bytes to construct the string 79 | * @param flags flags controlling methods used 80 | * @param b64flags flags for Base64 encoding 81 | * @returns the new random string, or NULL on error 82 | */ 83 | DECLEXPORT char * 84 | PSC_Random_createStr(size_t count, 85 | PSC_RandomFlags flags, PSC_Base64Flags b64flags) 86 | ATTR_MALLOC; 87 | 88 | #endif 89 | -------------------------------------------------------------------------------- /include/poser/core/ratelimit.h: -------------------------------------------------------------------------------- 1 | #ifndef POSER_CORE_RATELIMIT_H 2 | #define POSER_CORE_RATELIMIT_H 3 | 4 | /** Declarations for the PSC_RateLimit class 5 | * @file 6 | */ 7 | 8 | #include 9 | 10 | #include 11 | 12 | /** A simple rate limiter for actions identified by a key. 13 | * This class provides configurable rate-limiting with a set of individual 14 | * limits. Each limit consists of a number of seconds and a maximum number of 15 | * actions allowed within this time period. 16 | * 17 | * For counting, the time period is divided into equally sized slots with a 18 | * resolution of whole seconds. Up to 512 of these counting slots are created 19 | * per limit. So, the counting precision depends on the length of the time 20 | * period; the number of slots will be > 256 for each time period > 256 21 | * seconds. 22 | * 23 | * For an action to be allowed by the rate limiter, it must be allowed by all 24 | * the limits. Only those limits that allow the action will also count it. 25 | * @class PSC_RateLimit ratelimit.h 26 | */ 27 | C_CLASS_DECL(PSC_RateLimit); 28 | 29 | /** Options for creating a new rate limiter. 30 | * These options are used to create a new PSC_RateLimit instance. 31 | * @class PSC_RateLimitOpts ratelimit.h 32 | */ 33 | C_CLASS_DECL(PSC_RateLimitOpts); 34 | 35 | /** PSC_RateLimit default constructor. 36 | * Creates a new PSC_RateLimit 37 | * @memberof PSC_RateLimit 38 | * @param opts rate limit options 39 | * @returns a newly created PSC_RateLimit 40 | */ 41 | PSC_RateLimit * 42 | PSC_RateLimit_create(const PSC_RateLimitOpts *opts) 43 | ATTR_RETNONNULL; 44 | 45 | /** Check the limits for a given key and count. 46 | * Checks whether the action identified by the given key is at the current 47 | * time allowed according to all configured limits. For those limits that 48 | * allow it, it's also counted. 49 | * @memberof PSC_RateLimit 50 | * @param self the PSC_RateLimit 51 | * @param key identifier of the action 52 | * @param keysz size of the identifier 53 | * @returns 1 if the action is currently allowed, 0 otherwise 54 | */ 55 | int 56 | PSC_RateLimit_check(PSC_RateLimit *self, const void *key, size_t keysz) 57 | CMETHOD ATTR_NONNULL((1)); 58 | 59 | /** PSC_RateLimit destructor. 60 | * @memberof PSC_RateLimit 61 | * @param self the PSC_RateLimit 62 | */ 63 | void 64 | PSC_RateLimit_destroy(PSC_RateLimit *self); 65 | 66 | /** PSC_RateLimitOpts default constructor. 67 | * Creates a new PSC_RateLimitOpts 68 | * @memberof PSC_RateLimitOpts 69 | * @returns a newly created PSC_RateLimitOpts 70 | */ 71 | PSC_RateLimitOpts * 72 | PSC_RateLimitOpts_create(void) 73 | ATTR_RETNONNULL; 74 | 75 | /** Add an individual limit. 76 | * Configures a limit to allow at most @p limit actions in @p seconds seconds. 77 | * @memberof PSC_RateLimitOpts 78 | * @param self the PSC_RateLimitOpts 79 | * @param seconds number of seconds (min: 1, max: 65535) 80 | * @param limit number of actions (min: 1, max: 65535) 81 | * @returns 0 on success, -1 on error 82 | */ 83 | int 84 | PSC_RateLimitOpts_addLimit(PSC_RateLimitOpts *self, int seconds, int limit) 85 | CMETHOD; 86 | 87 | /** Compare rate limit options to another instance. 88 | * @memberof PSC_RateLimitOpts 89 | * @param self the PSC_RateLimitOpts 90 | * @param other another PSC_RateLimitOpts instance to compare to 91 | * @returns 1 if both instances are configured the same, 0 otherwise 92 | */ 93 | int 94 | PSC_RateLimitOpts_equals(const PSC_RateLimitOpts *self, 95 | const PSC_RateLimitOpts *other) 96 | CMETHOD ATTR_NONNULL((2)); 97 | 98 | /** PSC_RateLimitOpts destructor. 99 | * @memberof PSC_RateLimitOpts 100 | * @param self the PSC_RateLimitOpts 101 | */ 102 | void PSC_RateLimitOpts_destroy(PSC_RateLimitOpts *self); 103 | 104 | #endif 105 | -------------------------------------------------------------------------------- /include/poser/core/resolver.h: -------------------------------------------------------------------------------- 1 | #ifndef POSER_CORE_RESOLVER_H 2 | #define POSER_CORE_RESOLVER_H 3 | 4 | /** declarations for the PSC_Resolver class 5 | * @file 6 | */ 7 | 8 | #include 9 | 10 | /** A resolver to do a batch of reverse DNS lookups. 11 | * The resolver allows adding PSC_IpAddr instances and offers a method to 12 | * do reverse lookups for all of them in a batch, preferably using a thread 13 | * job. 14 | * @class PSC_Resolver resolver.h 15 | */ 16 | C_CLASS_DECL(PSC_Resolver); 17 | 18 | /** A single entry for the PSC_Resolver. 19 | * This just holds a PSC_IpAddr and, if a successful reverse lookup was done, 20 | * a corresponding name. 21 | * @class PSC_ResolverEntry resolver.h 22 | */ 23 | C_CLASS_DECL(PSC_ResolverEntry); 24 | 25 | C_CLASS_DECL(PSC_Event); 26 | C_CLASS_DECL(PSC_IpAddr); 27 | C_CLASS_DECL(PSC_List); 28 | 29 | /** PSC_Resolver default constructor. 30 | * Creates a new PSC_Resolver 31 | * @memberof PSC_Resolver 32 | * @returns a newly created PSC_Resolver 33 | */ 34 | DECLEXPORT PSC_Resolver * 35 | PSC_Resolver_create(void) 36 | ATTR_RETNONNULL; 37 | 38 | /** Add an address. 39 | * Add an address to the list of addresses to be resolved. Fails if a resolver 40 | * job is already running. 41 | * @memberof PSC_Resolver 42 | * @param self the PSC_Resolver 43 | * @param addr the address to add 44 | * @returns 0 on success, -1 on error 45 | */ 46 | DECLEXPORT int 47 | PSC_Resolver_addAddr(PSC_Resolver *self, const PSC_IpAddr *addr) 48 | CMETHOD ATTR_NONNULL((2)); 49 | 50 | /** Start resolving. 51 | * Starts batch resolving, prefers to do this on a thread job, but does it 52 | * synchronously if the thread pool is not available or the job can't be 53 | * enqueued. Fails if a resolver job is already running or there are no 54 | * addresses added to the resolver. 55 | * @memberof PSC_Resolver 56 | * @param self the PSC_Resolver 57 | * @param forceAsync if set to 1, fails when resolving can't be done on a 58 | * thread job 59 | * @returns 0 on success, -1 on error 60 | */ 61 | DECLEXPORT int 62 | PSC_Resolver_resolve(PSC_Resolver *self, int forceAsync) 63 | CMETHOD; 64 | 65 | /** Resolving finished. 66 | * This event fires when resolving is completed. Register at least one handler 67 | * for this event before calling PSC_Resolver_resolve(). 68 | * @memberof PSC_Resolver 69 | * @param self the PSC_Resolver 70 | * @returns the done event 71 | */ 72 | DECLEXPORT PSC_Event * 73 | PSC_Resolver_done(PSC_Resolver *self) 74 | CMETHOD ATTR_PURE ATTR_RETNONNULL; 75 | 76 | /** List of addresses and resolved names. 77 | * This list contains PSC_ResolverEntry instances for every PSC_IpAddr added, 78 | * with the corresponding names if successfully resolved. 79 | * @memberof PSC_Resolver 80 | * @param self the PSC_Resolver 81 | * @returns a list of PSC_ResolveEntry 82 | */ 83 | DECLEXPORT const PSC_List * 84 | PSC_Resolver_entries(const PSC_Resolver *self) 85 | CMETHOD ATTR_PURE ATTR_RETNONNULL; 86 | 87 | /** PSC_Resolver destructor. 88 | * Destroys the PSC_Resolver instance. If a resolve job is currently running, 89 | * this job is canceled and destructions only completes after the canceled 90 | * job actually stopped. Avoid doing this, it can't be 100% reliable. 91 | * @memberof PSC_Resolver 92 | * @param self the PSC_Resolver 93 | */ 94 | DECLEXPORT void 95 | PSC_Resolver_destroy(PSC_Resolver *self); 96 | 97 | /** The IP address of the entry. 98 | * @memberof PSC_ResolverEntry 99 | * @param self the PSC_ResolverEntry 100 | * @returns the PSC_IpAddr originally added to the PSC_Resolver 101 | */ 102 | DECLEXPORT const PSC_IpAddr * 103 | PSC_ResolverEntry_addr(const PSC_ResolverEntry *self) 104 | CMETHOD ATTR_PURE ATTR_RETNONNULL; 105 | 106 | /** The resolved name of the entry. 107 | * @memberof PSC_ResolverEntry 108 | * @param self the PSC_ResolverEntry 109 | * @returns the resolved name for an IP address, or NULL if resolving didn't 110 | * succeed or no resolving was done yet 111 | */ 112 | DECLEXPORT const char * 113 | PSC_ResolverEntry_name(const PSC_ResolverEntry *self) 114 | CMETHOD ATTR_PURE; 115 | 116 | #endif 117 | -------------------------------------------------------------------------------- /include/poser/core/runopts.h: -------------------------------------------------------------------------------- 1 | #ifndef POSER_CORE_RUNOPTS_H 2 | #define POSER_CORE_RUNOPTS_H 3 | 4 | /** declarations for the PSC_RunOpts class 5 | * @file 6 | */ 7 | 8 | #include 9 | 10 | /** Options for running a service. 11 | * These options are used by PSC_Service and PSC_Daemon. 12 | * @class PSC_RunOpts runopts.h 13 | */ 14 | 15 | /** Initialize to default values. 16 | * Resets all run options to their default values. If this isn't explicitly 17 | * called, options are automatically initialized without a pidfile. 18 | * @memberof PSC_RunOpts 19 | * @static 20 | * @param pidfile the pidfile to use, or NULL for none 21 | */ 22 | DECLEXPORT void 23 | PSC_RunOpts_init(const char *pidfile); 24 | 25 | /** Run as a different user. 26 | * When a different user is set here, an attempt is made to switch to it 27 | * during service startup. This will only work when the service was launched 28 | * by the superuser (root). 29 | * @memberof PSC_RunOpts 30 | * @static 31 | * @param uid user-id to switch to, or -1 for no change 32 | * @param gid group-id to switch to, or -1 for no change 33 | */ 34 | DECLEXPORT void 35 | PSC_RunOpts_runas(long uid, long gid); 36 | 37 | /** Handle logging using default options. 38 | * This will automatically configure logging during service startup. 39 | * 40 | * When running daemonized (default), it will log to syslog as well as stderr 41 | * and stop logging to stderr as soon as the service startup completed. It 42 | * will also enable asynchronous logging after successful startup. 43 | * 44 | * When running in foreground, it will just configure logging to stderr. 45 | * @memberof PSC_RunOpts 46 | * @static 47 | * @param logident name to use for syslog logging (default: posercore) 48 | */ 49 | DECLEXPORT void PSC_RunOpts_enableDefaultLogging(const char *logident); 50 | 51 | /** Run in foreground. 52 | * When this is set, PSC_Daemon_run() will directly call the daemon main 53 | * function without forking or handling a pidfile. 54 | * @memberof PSC_RunOpts 55 | * @static 56 | */ 57 | DECLEXPORT void 58 | PSC_RunOpts_foreground(void); 59 | 60 | /** Don't wait for successful service startup. 61 | * When this is set, the parent process will exit immediately instead of 62 | * waiting for PSC_Daemon_launched() to be called. 63 | * @memberof PSC_RunOpts 64 | * @static 65 | */ 66 | DECLEXPORT void 67 | PSC_RunOpts_nowait(void); 68 | 69 | /** Enable or disable using worker threads for additional event loops. 70 | * When this is set to a non-zero value, the service will launch additional 71 | * threads running their own event loop. These threads will be used for 72 | * handling connections accepted by a PSC_Server. 73 | * @memberof PSC_RunOpts 74 | * @static 75 | * @param workerThreads the number of worker threads to launch. If this number 76 | * is negative, launch as many worker threads as there 77 | * are CPU cores available, unless that number cannot be 78 | * determined, than use the absolute value as a fallback. 79 | */ 80 | DECLEXPORT void 81 | PSC_RunOpts_workerThreads(int workerThreads); 82 | 83 | #endif 84 | -------------------------------------------------------------------------------- /include/poser/core/server.h: -------------------------------------------------------------------------------- 1 | #ifndef POSER_CORE_SERVER_H 2 | #define POSER_CORE_SERVER_H 3 | 4 | /** declarations for the PSC_Server class 5 | * @file 6 | */ 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | /** A server listening on a socket and accepting connections. 14 | * This class will open one or multiple listening sockets and handle incoming 15 | * connections by creating a PSC_Connection for them and firing an event. 16 | * @class PSC_Server server.h 17 | */ 18 | C_CLASS_DECL(PSC_Server); 19 | 20 | /** Options for creating a TCP server. 21 | * @class PSC_TcpServerOpts server.h 22 | */ 23 | C_CLASS_DECL(PSC_TcpServerOpts); 24 | 25 | /** Options for creating a local UNIX server. 26 | * @class PSC_UnixServerOpts server.h 27 | */ 28 | C_CLASS_DECL(PSC_UnixServerOpts); 29 | 30 | C_CLASS_DECL(PSC_Connection); 31 | 32 | /** A callback executed for each accepted client connection 33 | * @param obj optional object reference for the "owner" object of the callback 34 | * @param conn the newly created client connection 35 | */ 36 | typedef void (*PSC_ClientConnectedCallback)(void *obj, PSC_Connection *conn); 37 | 38 | /** PSC_TcpServerOpts constructor. 39 | * Creates an options object initialized to default values. 40 | * @memberof PSC_TcpServerOpts 41 | * @param port the port to listen on 42 | * @returns a newly created options object 43 | */ 44 | DECLEXPORT PSC_TcpServerOpts * 45 | PSC_TcpServerOpts_create(int port) 46 | ATTR_RETNONNULL; 47 | 48 | /** Bind to a specific hostname or address. 49 | * This can be called multiple times to bind to multiple names or addresses. 50 | * If it isn't called at all, the server will listen on any interface/address. 51 | * @memberof PSC_TcpServerOpts 52 | * @param self the PSC_TcpServerOpts 53 | * @param bindhost hostname or address to bind to 54 | */ 55 | DECLEXPORT void 56 | PSC_TcpServerOpts_bind(PSC_TcpServerOpts *self, const char *bindhost) 57 | CMETHOD ATTR_NONNULL((2)); 58 | 59 | /** Set read buffer size. 60 | * Sets the size of the buffer used for connections accepted from this server, 61 | * in bytes. The default value is 16 kiB. 62 | * @memberof PSC_TcpServerOpts 63 | * @param self the PSC_TcpServerOpts 64 | * @param sz the size of the read buffer, must be > 0 65 | */ 66 | DECLEXPORT void 67 | PSC_TcpServerOpts_readBufSize(PSC_TcpServerOpts *self, size_t sz) 68 | CMETHOD; 69 | 70 | /** Enable TLS for the server. 71 | * Causes TLS to be enabled for any incoming connection, using a server 72 | * certificate. Note the certificate is required. 73 | * @memberof PSC_TcpServerOpts 74 | * @param self the PSC_TcpServerOpts 75 | * @param certfile certificate file for the server certificate 76 | * @param keyfile private key file for the server certificate 77 | */ 78 | DECLEXPORT void 79 | PSC_TcpServerOpts_enableTls(PSC_TcpServerOpts *self, 80 | const char *certfile, const char *keyfile) 81 | CMETHOD ATTR_NONNULL((2)) ATTR_NONNULL((3)); 82 | 83 | /** Enable checking of an optional client certificate. 84 | * If the client presents a client certificate, enable checking it. When a CA 85 | * file is given, the certificate must be issued from one of the CAs contained 86 | * in it. When the client presents a client certificate that doesn't validate, 87 | * handshake fails. 88 | * 89 | * If no CA file is given, any client certificate will fail validation unless 90 | * a custom validation function is configured with 91 | * PSC_TcpServerOpts_validateClientCert(). 92 | * 93 | * To strictly require a client certificate, use 94 | * PSC_TcpServerOpts_requireClientCert() instead. 95 | * @memberof PSC_TcpServerOpts 96 | * @param self the PSC_TcpServerOpts 97 | * @param cafile CA file (containing PEM certificates) 98 | */ 99 | DECLEXPORT void 100 | PSC_TcpServerOpts_enableClientCert(PSC_TcpServerOpts *self, 101 | const char *cafile) 102 | CMETHOD; 103 | 104 | /** Request a certificate from connecting clients. 105 | * Causes the server to request a client certificate from every connecting 106 | * client. If the client doesn't present a certificate, or the certificate 107 | * is not signed by a CA present in the given CA file, handshake fails. 108 | * 109 | * If no CA file is given, any client certificate will fail validation unless 110 | * a custom validation function is configured with 111 | * PSC_TcpServerOpts_validateClientCert(). 112 | * 113 | * To optionally enable validation of a client certificate if presented, use 114 | * PSC_TcpServerOpts_enableClientCert() instead. 115 | * @memberof PSC_TcpServerOpts 116 | * @param self the PSC_TcpServerOpts 117 | * @param cafile CA file (containing PEM certificates) 118 | */ 119 | DECLEXPORT void 120 | PSC_TcpServerOpts_requireClientCert(PSC_TcpServerOpts *self, 121 | const char *cafile) 122 | CMETHOD; 123 | 124 | /** Configure a custom validator for client certificates. 125 | * When this is used, the given validator will be called after default 126 | * validation of client certificates, so the application can still reject 127 | * or accept certificates based on custom logic. 128 | * 129 | * One of PSC_TcpServerOpts_enableClientCert() or 130 | * PSC_TcpServerOpts_requireClientCert() must be called for this to have any 131 | * effect. If a CA file is given there, this callback will only be called 132 | * after successful validation against the CA file. 133 | * @memberof PSC_TcpServerOpts 134 | * @param self the PSC_TcpServerOpts 135 | * @param receiver the object handling the validation (or 0 for static) 136 | * @param validator the custom validator function 137 | */ 138 | DECLEXPORT void 139 | PSC_TcpServerOpts_validateClientCert(PSC_TcpServerOpts *self, void *receiver, 140 | PSC_CertValidator validator) 141 | CMETHOD ATTR_NONNULL((3)); 142 | 143 | /** Set a specific protocol (IPv4 or IPv6). 144 | * @memberof PSC_TcpServerOpts 145 | * @param self the PSC_TcpServerOpts 146 | * @param proto protocol the server should use 147 | */ 148 | DECLEXPORT void 149 | PSC_TcpServerOpts_setProto(PSC_TcpServerOpts *self, PSC_Proto proto) 150 | CMETHOD; 151 | 152 | /** PSC_TcpServerOpts destructor. 153 | * @memberof PSC_TcpServerOpts 154 | * @param self the PSC_TcpServerOpts 155 | */ 156 | DECLEXPORT void 157 | PSC_TcpServerOpts_destroy(PSC_TcpServerOpts *self); 158 | 159 | /** PSC_UnixServerOpts constructor. 160 | * Creates an options object initialized to default values. 161 | * @memberof PSC_UnixServerOpts 162 | * @param name the file name (path) of the socket to listen on 163 | * @returns a newly created options object 164 | */ 165 | DECLEXPORT PSC_UnixServerOpts * 166 | PSC_UnixServerOpts_create(const char *name) 167 | ATTR_RETNONNULL ATTR_NONNULL((1)); 168 | 169 | /** Set read buffer size. 170 | * Sets the size of the buffer used for connections accepted from this server, 171 | * in bytes. The default value is 16 kiB. 172 | * @memberof PSC_UnixServerOpts 173 | * @param self the PSC_UnixServerOpts 174 | * @param sz the size of the read buffer, must be > 0 175 | */ 176 | DECLEXPORT void 177 | PSC_UnixSeverOpts_readBufSize(PSC_UnixServerOpts *self, size_t sz) 178 | CMETHOD; 179 | 180 | /** Set ownership of the UNIX socket. 181 | * When set, an attempt is made to change ownership of the socket. 182 | * @memberof PSC_UnixServerOpts 183 | * @param self the PSC_UnixServerOpts 184 | * @param uid desired owner for the socket, -1 for no change 185 | * @param gid desired group for the socket, -1 for no change 186 | */ 187 | DECLEXPORT void 188 | PSC_UnixServerOpts_owner(PSC_UnixServerOpts *self, int uid, int gid) 189 | CMETHOD; 190 | 191 | /** Set access mode for the UNIX socket. 192 | * When not set, the default mode is 0600, so only the owner can interact with 193 | * the socket. 194 | * @memberof PSC_UnixServerOpts 195 | * @param self the PSC_UnixServerOpts 196 | * @param mode desired access mode 197 | */ 198 | DECLEXPORT void 199 | PSC_UnixServerOpts_mode(PSC_UnixServerOpts *self, int mode) 200 | CMETHOD; 201 | 202 | /** PSC_UnixServerOpts destructor. 203 | * @memberof PSC_UnixServerOpts 204 | * @param self the PSC_UnixServerOpts 205 | */ 206 | DECLEXPORT void 207 | PSC_UnixServerOpts_destroy(PSC_UnixServerOpts *self); 208 | 209 | /** Create a TCP server. 210 | * @memberof PSC_Server 211 | * @param opts TCP server options 212 | * @param owner optional owner reference passed to the callbacks for newly 213 | * created clients and completed shutdown 214 | * @param clientConnected callback executed for each connected client 215 | * @param shutdownComplete callback executed when a PSC_Server_shutdown() call 216 | * completed 217 | * @returns a newly created server, or NULL on error 218 | */ 219 | DECLEXPORT PSC_Server * 220 | PSC_Server_createTcp(const PSC_TcpServerOpts *opts, void *owner, 221 | PSC_ClientConnectedCallback clientConnected, 222 | void (*shutdownComplete)(void *)) 223 | ATTR_NONNULL((1)) ATTR_NONNULL((3)); 224 | 225 | /** Reconfigure a running TCP server. 226 | * Try to apply a new configuration to an already running server. The port, 227 | * protocol preference, read buffer size and list of bind addresses cannot 228 | * be changed at runtime. If the configuration is the same as before, this 229 | * silently succeeds. 230 | * @memberof PSC_Server 231 | * @param self the PSC_Server 232 | * @param opts the new TCP server options 233 | * @returns 0 on success, -1 if the new configuration can't be applied or the 234 | * server is not a TCP server. 235 | */ 236 | DECLEXPORT int 237 | PSC_Server_configureTcp(PSC_Server *self, const PSC_TcpServerOpts *opts) 238 | CMETHOD ATTR_NONNULL((1)); 239 | 240 | /** Create a local UNIX server. 241 | * @memberof PSC_Server 242 | * @param opts UNIX server options 243 | * @param owner optional owner reference passed to the callbacks for newly 244 | * created clients and completed shutdown 245 | * @param clientConnected callback executed for each connected client 246 | * @param shutdownComplete callback executed when a PSC_Server_shutdown() call 247 | * completed 248 | * @returns a newly created server, or NULL on error 249 | */ 250 | DECLEXPORT PSC_Server * 251 | PSC_Server_createUnix(const PSC_UnixServerOpts *opts, void *owner, 252 | PSC_ClientConnectedCallback clientConnected, 253 | void (*shutdownComplete)(void *)) 254 | ATTR_NONNULL((1)) ATTR_NONNULL((3)); 255 | 256 | /** Disable the server. 257 | * This disables accepting new connections while still listening. It's 258 | * implemented by immediately closing any new connection with a linger timeout 259 | * of 0, which should be signaled as an error to the client trying to connect. 260 | * For a TCP server, it means immediately sending an RST packet. 261 | * 262 | * Note this does not affect already existing connections. 263 | * @memberof PSC_Server 264 | * @param self the PSC_Server 265 | */ 266 | DECLEXPORT void 267 | PSC_Server_disable(PSC_Server *self) 268 | CMETHOD; 269 | 270 | /** Enable the server. 271 | * This enables accepting new connections again after PSC_Server_disable() was 272 | * called. 273 | * @memberof PSC_Server 274 | * @param self the PSC_Server 275 | */ 276 | DECLEXPORT void 277 | PSC_Server_enable(PSC_Server *self) 278 | CMETHOD; 279 | 280 | /** Number of connections to this server. 281 | * @memberof PSC_Server 282 | * @param self the PSC_Server 283 | * @returns the current number of connections 284 | */ 285 | DECLEXPORT size_t 286 | PSC_Server_connections(const PSC_Server *self) 287 | CMETHOD; 288 | 289 | /** Graceful server shutdown. 290 | * This will stop listening, but defer destruction until all client 291 | * connections are closed or the given timeout is reached. The server is 292 | * finally destroyed. This could also happen immediately if there aren't any 293 | * active client connections, so the object should be considered invalid 294 | * after calling this method. 295 | * @memberof PSC_Server 296 | * @param self the PSC_Server 297 | * @param timeout timeout in milliseconds before forcing destructions. If set 298 | * to 0, destruction is not forced until the PSC_Service exits. 299 | */ 300 | DECLEXPORT void 301 | PSC_Server_shutdown(PSC_Server *self, unsigned timeout); 302 | 303 | /** PSC_Server destructor. 304 | * This will close all active client connections and stop listening. 305 | * @memberof PSC_Server 306 | * @param self the PSC_Server 307 | */ 308 | DECLEXPORT void 309 | PSC_Server_destroy(PSC_Server *self); 310 | 311 | #endif 312 | -------------------------------------------------------------------------------- /include/poser/core/stringbuilder.h: -------------------------------------------------------------------------------- 1 | #ifndef POSER_CORE_STRINGBUILDER_H 2 | #define POSER_CORE_STRINGBUILDER_H 3 | 4 | #include 5 | 6 | /** declarations for the PSC_StringBuilder class 7 | * @file 8 | */ 9 | 10 | /** A simple string builder 11 | * @class PSC_StringBuilder stringbuilder.h 12 | */ 13 | C_CLASS_DECL(PSC_StringBuilder); 14 | 15 | /** PSC_StringBuilder default constructor. 16 | * Creates a new PSC_StringBuilder 17 | * @memberof PSC_StringBuilder 18 | * @returns a newly created PSC_StringBuilder 19 | */ 20 | DECLEXPORT PSC_StringBuilder * 21 | PSC_StringBuilder_create(void) 22 | ATTR_RETNONNULL; 23 | 24 | /** Append a string to the builder. 25 | * @memberof PSC_StringBuilder 26 | * @param self the PSC_StringBuilder 27 | * @param str the string to append 28 | */ 29 | DECLEXPORT void 30 | PSC_StringBuilder_append(PSC_StringBuilder *self, const char *str) 31 | CMETHOD ATTR_NONNULL((2)); 32 | 33 | /** Append a single character to the builder. 34 | * @memberof PSC_StringBuilder 35 | * @param self the PSC_StringBuilder 36 | * @param c the character to append 37 | */ 38 | DECLEXPORT void 39 | PSC_StringBuilder_appendChar(PSC_StringBuilder *self, char c) 40 | CMETHOD; 41 | 42 | /** Get the complete string. 43 | * @memberof PSC_StringBuilder 44 | * @param self the PSC_StringBuilder 45 | * @returns the complete string 46 | */ 47 | DECLEXPORT const char * 48 | PSC_StringBuilder_str(const PSC_StringBuilder *self) 49 | CMETHOD ATTR_RETNONNULL ATTR_PURE; 50 | 51 | /** PSC_StringBuilder destructor. 52 | * @memberof PSC_StringBuilder 53 | * @param self the PSC_StringBuilder 54 | */ 55 | DECLEXPORT void 56 | PSC_StringBuilder_destroy(PSC_StringBuilder *self); 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /include/poser/core/threadpool.h: -------------------------------------------------------------------------------- 1 | #ifndef POSER_CORE_THREADPOOL_H 2 | #define POSER_CORE_THREADPOOL_H 3 | 4 | /** declarations for the PSC_ThreadPool and related classes 5 | * @file 6 | */ 7 | 8 | #include 9 | 10 | /** A job to be executed on a worker thread. 11 | * @class PSC_ThreadJob threadpool.h 12 | */ 13 | C_CLASS_DECL(PSC_ThreadJob); 14 | 15 | /** An asynchronous task a thread job can wait for. 16 | * When the library is built on a system supporting user context switching 17 | * with POSIX-1.2001 getcontext() and friends and the thread job has async 18 | * behavior enabled by calling PSC_ThreadJob_setAsync(), the thread is 19 | * released for other thread jobs while waiting, otherwise the worker thread 20 | * will be blocked. 21 | * 22 | * No destructor is offered, the task destroys itself on completion. 23 | * @class PSC_AsyncTask threadpool.h 24 | */ 25 | C_CLASS_DECL(PSC_AsyncTask); 26 | 27 | /** Options for PSC_ThreadPool. 28 | * This class is for configuring the thread pool. If none of its methods are 29 | * called, compile-time default values are used. 30 | * @class PSC_ThreadOpts threadpool.h 31 | */ 32 | 33 | /** A thread pool. 34 | * This class creates a fixed set of worker threads and a queue of 35 | * PSC_ThreadJob objects for jobs to be executed on a worker thread. An event 36 | * will be fired when a job completes. Running jobs can also be canceled. 37 | * @class PSC_ThreadPool threadpool.h 38 | */ 39 | 40 | C_CLASS_DECL(PSC_Event); 41 | 42 | /** A function to run on a worker thread. 43 | * @param arg the data to work on 44 | */ 45 | typedef void (*PSC_ThreadProc)(void *arg); 46 | 47 | /** A function to run for completing an asynchronous task. 48 | * @param task the task to complete 49 | */ 50 | typedef void (*PSC_AsyncTaskJob)(PSC_AsyncTask *task); 51 | 52 | /** Create a new thread job. 53 | * Creates a new job to be executed on a worker thread. Unless the library was 54 | * built on a system without POSIX user context switching support, the job may 55 | * execute on its own private stack with a default size of 2 MiB, allowing 56 | * a PSC_AsyncTask to release the thread for other work while waiting. To 57 | * enable this behavior, call PSC_ThreadJob_setAsync(), 58 | * @memberof PSC_ThreadJob 59 | * @param proc the function to run on the worker thread 60 | * @param arg the data to work on 61 | * @param timeoutMs if non-zero, automatically cancel the job after this many 62 | * milliseconds passed 63 | * @returns a newly created thread job, or NULL on error 64 | */ 65 | DECLEXPORT PSC_ThreadJob * 66 | PSC_ThreadJob_create(PSC_ThreadProc proc, void *arg, int timeoutMs) 67 | ATTR_NONNULL((1)); 68 | 69 | /** Enable private stack for the job, 70 | * Enable creating/using a private stack for this job. If not enabled, 71 | * awaiting some PSC_AsyncTask will block a worker thread even if the library 72 | * was built on a system with POSIX user context switching support. 73 | * 74 | * It's recommended to only enable this for jobs that might eventually await 75 | * a PSC_AsyncTask, because it incurs some overhead already on starting and 76 | * finishing the job. 77 | * @memberof PSC_ThreadJob 78 | * @param self the PSC_ThreadJob 79 | */ 80 | DECLEXPORT void 81 | PSC_ThreadJob_setAsync(PSC_ThreadJob *self) 82 | CMETHOD; 83 | 84 | /** The job finished. 85 | * This event fires when the thread job finished, either because it completed 86 | * or because it was canceled. 87 | * @memberof PSC_ThreadJob 88 | * @param self the PSC_ThreadJob 89 | * @returns the finished event 90 | */ 91 | DECLEXPORT PSC_Event * 92 | PSC_ThreadJob_finished(PSC_ThreadJob *self) 93 | CMETHOD ATTR_RETNONNULL ATTR_PURE; 94 | 95 | /** Determine whether the job completed. 96 | * This can be called when PSC_ThreadJob_finished() fired to know whether the 97 | * job completed successfully. 98 | * @memberof PSC_ThreadJob 99 | * @param self the PSC_ThreadJob 100 | * @returns 1 if the job completed, 0 otherwise 101 | */ 102 | DECLEXPORT int 103 | PSC_ThreadJob_hasCompleted(const PSC_ThreadJob *self) 104 | CMETHOD ATTR_PURE; 105 | 106 | /** PSC_ThreadJob destructor. 107 | * Destroys the thread job. 108 | * 109 | * Note that once the job was scheduled, PSC_ThreadPool will automatically 110 | * destroy it when it completed or is canceled. 111 | * @memberof PSC_ThreadJob 112 | * @param self the PSC_ThreadJob 113 | */ 114 | DECLEXPORT void 115 | PSC_ThreadJob_destroy(PSC_ThreadJob *self); 116 | 117 | /** Check whether a job was canceled. 118 | * This must only be called from within a PSC_ThreadProc. It signals whether 119 | * the job was canceled, in that case, the PSC_ThreadProc should exit quickly. 120 | * @memberof PSC_ThreadJob 121 | * @static 122 | * @returns 1 when the job was canceled, 0 otherwise 123 | */ 124 | DECLEXPORT int 125 | PSC_ThreadJob_canceled(void); 126 | 127 | /** Check whether PSC_AsyncTask_await() will always block. 128 | * This method tells at runtime whether awaiting a PSC_AsyncTask will always 129 | * block the worker thread (which is the case on systems without support for 130 | * POSIX user context switching), regardless of PSC_ThreadJob_setAsync(). 131 | * It may be used to configure a suitable number of worker threads per CPU. 132 | * @memberof PSC_AsyncTask 133 | * @static 134 | * @returns 1 if PSC_AsyncTask_await() always blocks, 0 if it only blocks when 135 | * a stack size of 0 is configured. 136 | */ 137 | DECLEXPORT int 138 | PSC_AsyncTask_awaitIsBlocking(void); 139 | 140 | /** PSC_AsyncTask default constructor. 141 | * Creates a new PSC_AsyncTask that will call the given function on the main 142 | * thread when awaited. 143 | * @memberof PSC_AsyncTask 144 | * @param job the function to call on the main thread 145 | * @returns a newly created PSC_AsyncTask 146 | */ 147 | DECLEXPORT PSC_AsyncTask * 148 | PSC_AsyncTask_create(PSC_AsyncTaskJob job); 149 | 150 | /** Wait for completion of an async task. 151 | * @memberof PSC_AsyncTask 152 | * @param self the PSC_AsyncTask 153 | * @param arg an optional argument to pass to the async task 154 | * @returns the result given to PSC_AsyncTask_complete() 155 | */ 156 | DECLEXPORT void * 157 | PSC_AsyncTask_await(PSC_AsyncTask *self, void *arg); 158 | 159 | /** Get the argument of an async task. 160 | * This is meant to be called on the main thread from a PSC_AsyncTaskJob 161 | * to get the optional argument passed to PSC_AsyncTask_await(). 162 | * @memberof PSC_AsyncTask 163 | * @param self the PSC_AsyncTask 164 | * @returns the task's argument 165 | */ 166 | DECLEXPORT void * 167 | PSC_AsyncTask_arg(PSC_AsyncTask *self); 168 | 169 | /** Complete an async task. 170 | * This must be called from the main thread to allow the thread job awaiting 171 | * the async task to continue. 172 | * @memberof PSC_AsyncTask 173 | * @param self the PSC_AsyncTask 174 | * @param result an optional result to pass to the awaiting job 175 | */ 176 | DECLEXPORT void 177 | PSC_AsyncTask_complete(PSC_AsyncTask *self, void *result); 178 | 179 | /** Initialize options with default values. 180 | * @memberof PSC_ThreadOpts 181 | * @static 182 | * @param defThreads default number of threads to create when number of CPUs 183 | * can't be determined 184 | */ 185 | DECLEXPORT void 186 | PSC_ThreadOpts_init(int defThreads); 187 | 188 | /** Set a fixed numer of threads. 189 | * @memberof PSC_ThreadOpts 190 | * @static 191 | * @param n always create this many threads 192 | */ 193 | DECLEXPORT void 194 | PSC_ThreadOpts_fixedThreads(int n); 195 | 196 | /** Set number of threads per CPU. 197 | * If the number of CPUs can be determined and there's no fixed number of 198 | * threads configured, the number of threads created will be a multiple of the 199 | * number of CPUs. Default is 1. 200 | * @memberof PSC_ThreadOpts 201 | * @static 202 | * @param n create n threads per detected CPU 203 | */ 204 | DECLEXPORT void 205 | PSC_ThreadOpts_threadsPerCpu(int n); 206 | 207 | /** Set maximum number of threads. 208 | * @memberof PSC_ThreadOpts 209 | * @static 210 | * @param n never create more than n threads 211 | */ 212 | DECLEXPORT void 213 | PSC_ThreadOpts_maxThreads(int n); 214 | 215 | /** Set a fixed queue size for waiting thread jobs. 216 | * @memberof PSC_ThreadOpts 217 | * @static 218 | * @param n fixed size of the queue 219 | */ 220 | DECLEXPORT void 221 | PSC_ThreadOpts_fixedQueue(int n); 222 | 223 | /** Set queue size for waiting jobs per thread. 224 | * If no fixed queue size is configured, the queue size will be a multiple of 225 | * the number of threads created. Default is 2. 226 | * @memberof PSC_ThreadOpts 227 | * @static 228 | * @param n queue size per thread 229 | */ 230 | DECLEXPORT void 231 | PSC_ThreadOpts_queuePerThread(int n); 232 | 233 | /** Set maximum queue size for waiting thread jobs. 234 | * @memberof PSC_ThreadOpts 235 | * @static 236 | * @param n maximum size of the queue 237 | */ 238 | DECLEXPORT void 239 | PSC_ThreadOpts_maxQueue(int n); 240 | 241 | /** Set minimum queue size for waiting thread jobs. 242 | * @memberof PSC_ThreadOpts 243 | * @static 244 | * @param n minimum size of the queue 245 | */ 246 | DECLEXPORT void 247 | PSC_ThreadOpts_minQueue(int n); 248 | 249 | /** Initialize the thread pool. 250 | * This launches the worker threads, according to the configuration from 251 | * PSC_ThreadOpts. 252 | * @memberof PSC_ThreadPool 253 | * @static 254 | * @returns -1 on error, 0 on success 255 | */ 256 | DECLEXPORT int 257 | PSC_ThreadPool_init(void); 258 | 259 | /** Check whether thread pool is active. 260 | * @memberof PSC_ThreadPool 261 | * @static 262 | * @returns 1 when thread pool is active, 0 otherwise 263 | */ 264 | DECLEXPORT int 265 | PSC_ThreadPool_active(void); 266 | 267 | /** Enqueue a thread job. 268 | * If a worker thread is available, the job is started on it directly, 269 | * otherwise it is put in the queue of waiting jobs. 270 | * 271 | * Note this takes ownership of the PSC_ThreadJob object. 272 | * @memberof PSC_ThreadPool 273 | * @static 274 | * @param job the job to enqueue/start 275 | * @returns -1 on error (queue overflow), 0 on success 276 | */ 277 | DECLEXPORT int 278 | PSC_ThreadPool_enqueue(PSC_ThreadJob *job) 279 | ATTR_NONNULL((1)); 280 | 281 | /** Cancel a thread job. 282 | * If the job is already running, it is sent an interrupting signal and have a 283 | * flag set that can be checked with PSC_ThreadJob_canceled(). If it is still 284 | * waiting in the queue, it is just removed and destroyed. In any case, its 285 | * finished event will fire. 286 | * @memberof PSC_ThreadPool 287 | * @static 288 | * @param job the job to cancel 289 | */ 290 | DECLEXPORT void 291 | PSC_ThreadPool_cancel(PSC_ThreadJob *job) 292 | ATTR_NONNULL((1)); 293 | 294 | /** Stop the thread pool. 295 | * All worker threads are stopped. 296 | * @memberof PSC_ThreadPool 297 | * @static 298 | */ 299 | DECLEXPORT void 300 | PSC_ThreadPool_done(void); 301 | 302 | #endif 303 | -------------------------------------------------------------------------------- /include/poser/core/timer.h: -------------------------------------------------------------------------------- 1 | #ifndef POSER_CORE_TIMER_H 2 | #define POSER_CORE_TIMER_H 3 | 4 | #include 5 | 6 | #include 7 | 8 | /** declarations for the PSC_Timer class 9 | * @file 10 | */ 11 | 12 | /** A timer. 13 | * @class PSC_Timer timer.h 14 | */ 15 | C_CLASS_DECL(PSC_Timer); 16 | 17 | C_CLASS_DECL(PSC_Event); 18 | 19 | /** PSC_Timer default constructor. 20 | * Creates a new PSC_Timer 21 | * @memberof PSC_Timer 22 | * @returns a newly created PSC_Timer, or NULL on error 23 | */ 24 | DECLEXPORT PSC_Timer * 25 | PSC_Timer_create(void); 26 | 27 | /** The timer expired. 28 | * This event is fired on each expiry of the timer. 29 | * @memberof PSC_Timer 30 | * @param self the PSC_Timer 31 | * @returns the expired event 32 | */ 33 | DECLEXPORT PSC_Event * 34 | PSC_Timer_expired(PSC_Timer *self) 35 | CMETHOD ATTR_RETNONNULL ATTR_PURE; 36 | 37 | /** Set expiry in milliseconds. 38 | * Sets the expiry time in milliseconds. If the timer is currently 39 | * running, it is first stopped and then restarted with the new value. 40 | * The initial value is 1000 milliseconds (1 second). 41 | * @memberof PSC_Timer 42 | * @param self the PSC_Timer 43 | * @param ms expiry in milliseconds 44 | */ 45 | DECLEXPORT void 46 | PSC_Timer_setMs(PSC_Timer *self, unsigned ms) 47 | CMETHOD; 48 | 49 | /** Start the timer. 50 | * Starts the timer. An expired event will be fired after the configured. 51 | * If the timer is already running, stops and restarts it. 52 | * expiry time. 53 | * @memberof PSC_Timer 54 | * @param self the PSC_Timer 55 | * @param periodic if nonzero, expire periodically until explicitly stopped. 56 | */ 57 | DECLEXPORT void 58 | PSC_Timer_start(PSC_Timer *self, int periodic) 59 | CMETHOD; 60 | 61 | /** Stop the timer. 62 | * Stops the timer. No further expired events will occur until 63 | * PSC_Timer_start() is called again. 64 | * @memberof PSC_Timer 65 | * @param self the PSC_Timer 66 | */ 67 | DECLEXPORT void 68 | PSC_Timer_stop(PSC_Timer *self) 69 | CMETHOD; 70 | 71 | /** PSC_Timer destructor. 72 | * Destroys the timer. If it is currently running, it is stopped first. 73 | * @memberof PSC_Timer 74 | * @param self the PSC_Timer 75 | */ 76 | DECLEXPORT void 77 | PSC_Timer_destroy(PSC_Timer *self); 78 | 79 | #endif 80 | 81 | -------------------------------------------------------------------------------- /include/poser/core/util.h: -------------------------------------------------------------------------------- 1 | #ifndef POSER_CORE_UTIL_H 2 | #define POSER_CORE_UTIL_H 3 | 4 | /** generic utility functions 5 | * @file 6 | */ 7 | 8 | #include 9 | 10 | #include 11 | 12 | /** Allocate memory. 13 | * This is a wrapper around malloc() that ensures only valid pointers are 14 | * returned. In case of an allocation error, PSC_Service_panic() is called. 15 | * @param size size of the memory to allocate 16 | * @returns the newly allocated memory 17 | */ 18 | DECLEXPORT void * 19 | PSC_malloc(size_t size) 20 | ATTR_MALLOC ATTR_RETNONNULL ATTR_ALLOCSZ((1)); 21 | 22 | /** Re-allocate memory. 23 | * This is a wrapper around realloc() that ensures only valid pointers are 24 | * returned. In case of an allocation error, PSC_Service_panic() is called. 25 | * @param ptr the previously allocated memory 26 | * @param size new size for the allocated memory 27 | * @returns the newly allocated memory 28 | */ 29 | DECLEXPORT void * 30 | PSC_realloc(void *ptr, size_t size) 31 | ATTR_RETNONNULL ATTR_ALLOCSZ((2)); 32 | 33 | /** Copy a string. 34 | * This is similar to strdup(), but uses PSC_malloc() internally. 35 | * @param src the string to copy 36 | * @returns a newly allocated copy of the string 37 | */ 38 | DECLEXPORT char * 39 | PSC_copystr(const char *src) 40 | ATTR_MALLOC; 41 | 42 | /** Lowercase a string. 43 | * This creates a copy of the string in all lowercase. 44 | * @param src the string to copy 45 | * @returns a newly allocated lowercased string 46 | */ 47 | DECLEXPORT char * 48 | PSC_lowerstr(const char *src) 49 | ATTR_MALLOC; 50 | 51 | /** Join multiple strings. 52 | * This joins an array of strings into a new string, putting a delimeter 53 | * between all the elements. 54 | * @param delim the delimeter 55 | * @param strings the strings to join, array must end with a NULL pointer 56 | * @returns a newly allocated joined string 57 | */ 58 | DECLEXPORT char * 59 | PSC_joinstr(const char *delim, char **strings) 60 | ATTR_MALLOC ATTR_NONNULL((1)); 61 | 62 | /** Get base filename from full path. 63 | * This looks for the last path separator ('/') in the given string and 64 | * returns a pointer to the first character after it. If no separator is 65 | * found, the original string is returned. If the returned string would be 66 | * empty, a pointer to the string "." is returned instead. 67 | * @param path the full path string 68 | * @returns the base filename 69 | */ 70 | DECLEXPORT const char * 71 | PSC_basename(const char *path) 72 | ATTR_RETNONNULL ATTR_NONNULL((1)); 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /include/poser/decl.h: -------------------------------------------------------------------------------- 1 | #ifndef POSER_DECL_H 2 | #define POSER_DECL_H 3 | 4 | #undef poser___cdecl 5 | #undef SOEXPORT 6 | #undef SOLOCAL 7 | #undef DECLEXPORT 8 | 9 | #define ATTR_ACCESS(x) 10 | #define ATTR_ALLOCSZ(x) 11 | #define ATTR_CONST 12 | #define ATTR_FALLTHROUGH 13 | #define ATTR_FORMAT(x) 14 | #define ATTR_MALLOC 15 | #define ATTR_NONNULL(x) 16 | #define ATTR_NORETURN 17 | #define ATTR_RETNONNULL 18 | #define ATTR_PURE 19 | #define CMETHOD 20 | #define OBSOLETE(x) 21 | 22 | #ifdef __cplusplus 23 | # define poser___cdecl extern "C" 24 | # define DECLDATA 25 | # define C_CLASS_DECL(t) struct t 26 | #else 27 | # define poser___cdecl 28 | # define DECLDATA extern 29 | # define C_CLASS_DECL(t) typedef struct t t 30 | #endif 31 | 32 | #if defined __has_attribute 33 | # if __has_attribute (access) 34 | # undef ATTR_ACCESS 35 | # define ATTR_ACCESS(x) __attribute__ ((access x)) 36 | # endif 37 | # if __has_attribute (alloc_size) 38 | # undef ATTR_ALLOCSZ 39 | # define ATTR_ALLOCSZ(x) __attribute__ ((alloc_size x)) 40 | # endif 41 | # if __has_attribute (const) 42 | # undef ATTR_CONST 43 | # define ATTR_CONST __attribute__ ((const)) 44 | # endif 45 | # if __has_attribute (fallthrough) 46 | # undef ATTR_FALLTHROUGH 47 | # define ATTR_FALLTHROUGH __attribute__ ((fallthrough)) 48 | # endif 49 | # if __has_attribute (format) 50 | # undef ATTR_FORMAT 51 | # define ATTR_FORMAT(x) __attribute__ ((format x)) 52 | # endif 53 | # if __has_attribute (malloc) 54 | # undef ATTR_MALLOC 55 | # define ATTR_MALLOC __attribute__ ((malloc)) 56 | # endif 57 | # if __has_attribute (nonnull) 58 | # undef ATTR_NONNULL 59 | # undef CMETHOD 60 | # define ATTR_NONNULL(x) __attribute__ ((nonnull x)) 61 | # define CMETHOD __attribute__ ((nonnull (1))) 62 | # endif 63 | # if __has_attribute (noreturn) 64 | # undef ATTR_NORETURN 65 | # define ATTR_NORETURN __attribute__ ((noreturn)) 66 | # endif 67 | # if __has_attribute (returns_nonnull) 68 | # undef ATTR_RETNONNULL 69 | # define ATTR_RETNONNULL __attribute__ ((returns_nonnull)) 70 | # endif 71 | # if __has_attribute (pure) 72 | # undef ATTR_PURE 73 | # define ATTR_PURE __attribute__ ((pure)) 74 | # endif 75 | # if __has_attribute (visibility) 76 | # define SOEXPORT poser___cdecl __attribute__((visibility("default"))) 77 | # define SOLOCAL __attribute__((visibility("hidden"))) 78 | # else 79 | # define SOEXPORT poser___cdecl 80 | # define SOLOCAL 81 | # endif 82 | # if __has_attribute (deprecated) 83 | # undef OBSOLETE 84 | # define OBSOLETE(x) __attribute__ ((deprecated ( #x ))) 85 | # endif 86 | #endif 87 | #define DECLEXPORT poser___cdecl 88 | 89 | #endif 90 | -------------------------------------------------------------------------------- /src/lib/core/assert.h: -------------------------------------------------------------------------------- 1 | #ifndef POSER_CORE_INT_ASSERT_H 2 | #define POSER_CORE_INT_ASSERT_H 3 | 4 | #if defined(HAVE_BTINT) || defined(HAVE_BTSZT) 5 | # ifdef NDEBUG 6 | # define assert(x) 7 | # else 8 | # include 9 | # include 10 | # include 11 | # include 12 | # define assert(x) do { \ 13 | void *posercore_assert_stack[128]; \ 14 | if (!(x)) { \ 15 | fprintf(stderr, "Assertion failed: (" #x "), function %s, file " \ 16 | __FILE__ ", line %u.\n", __func__, __LINE__); \ 17 | int posercore_assert_stacksz = backtrace(posercore_assert_stack, 128); \ 18 | backtrace_symbols_fd(posercore_assert_stack, posercore_assert_stacksz, \ 19 | STDERR_FILENO); \ 20 | abort(); \ 21 | } \ 22 | } while (0) 23 | # endif 24 | #else 25 | # include 26 | #endif 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /src/lib/core/base64.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | static const char alphabet[] = 9 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/-_"; 10 | 11 | static uint8_t dec1(char val) ATTR_CONST; 12 | static char enc1(uint8_t val, PSC_Base64Flags flags) ATTR_CONST; 13 | 14 | static char enc1(uint8_t val, PSC_Base64Flags flags) 15 | { 16 | uint8_t pos = val & 0x3f; 17 | if ((flags & PSC_B64_URLSAFE) && pos >= 0x3e) pos += 2; 18 | return alphabet[pos]; 19 | } 20 | 21 | static uint8_t dec1(char val) 22 | { 23 | char *pos = strchr(alphabet, val); 24 | if (!pos) return 0xff; 25 | uint8_t dec = pos - alphabet; 26 | if (dec > 0x3f) dec -= 2; 27 | return dec; 28 | } 29 | 30 | SOEXPORT size_t PSC_Base64_encodedLen(size_t size) 31 | { 32 | size_t res = size % 3; 33 | if (res) ++res; 34 | return 4 * (size/3) + res; 35 | } 36 | 37 | SOEXPORT size_t PSC_Base64_decodedSize(size_t len) 38 | { 39 | size_t res = len % 4; 40 | if (res > 1) --res; 41 | return 3 * (len/4) + res; 42 | } 43 | 44 | SOEXPORT void PSC_Base64_encodeTo(char *enc, const void *data, size_t size, 45 | PSC_Base64Flags flags) 46 | { 47 | size_t pos = 0; 48 | const uint8_t *d = data; 49 | while (size-pos >= 3) 50 | { 51 | *enc++ = enc1(d[pos]>>2, flags); 52 | *enc++ = enc1(d[pos]<<4|d[pos+1]>>4, flags); 53 | *enc++ = enc1(d[pos+1]<<2|d[pos+2]>>6, flags); 54 | *enc++ = enc1(d[pos+2], flags); 55 | pos += 3; 56 | } 57 | if (size - pos == 2) 58 | { 59 | *enc++ = enc1(d[pos]>>2, flags); 60 | *enc++ = enc1(d[pos]<<4|d[pos+1]>>4, flags); 61 | *enc++ = enc1(d[pos+1]<<2, flags); 62 | } 63 | else if (pos < size) 64 | { 65 | *enc++ = enc1(d[pos]>>2, flags); 66 | *enc++ = enc1(d[pos]<<4, flags); 67 | } 68 | *enc = 0; 69 | } 70 | 71 | SOEXPORT char *PSC_Base64_encode(const void *data, size_t size, 72 | PSC_Base64Flags flags) 73 | { 74 | char *encoded = PSC_malloc(PSC_Base64_encodedLen(size) + 1); 75 | PSC_Base64_encodeTo(encoded, data, size, flags); 76 | return encoded; 77 | } 78 | 79 | SOEXPORT void PSC_Base64_decodeTo(void *data, const char *enc, size_t len) 80 | { 81 | size_t pos = 0; 82 | uint8_t *d = data; 83 | uint8_t b1, b2, b3; 84 | while (len - pos >= 4) 85 | { 86 | b1 = dec1(enc[pos++]); 87 | b2 = dec1(enc[pos++]); 88 | b3 = dec1(enc[pos++]); 89 | *d++ = b1<<2|b2>>4; 90 | *d++ = b2<<4|b3>>2; 91 | *d++ = b3<<6|dec1(enc[pos++]); 92 | } 93 | if (len - pos == 3) 94 | { 95 | b1 = dec1(enc[pos++]); 96 | b2 = dec1(enc[pos++]); 97 | b3 = dec1(enc[pos]); 98 | *d++ = b1<<2|b2>>4; 99 | *d = b2<<4|b3>>2; 100 | } 101 | else if (len - pos == 2) 102 | { 103 | b1 = dec1(enc[pos++]); 104 | *d = b1<<2|dec1(enc[pos])>>4; 105 | } 106 | } 107 | 108 | SOEXPORT void *PSC_Base64_decode(const char *enc, size_t *size) 109 | { 110 | size_t esz = strlen(enc); 111 | size_t dsz = PSC_Base64_decodedSize(esz); 112 | void *decoded = PSC_malloc(dsz); 113 | PSC_Base64_decodeTo(decoded, enc, esz); 114 | if (size) *size = dsz; 115 | return decoded; 116 | } 117 | 118 | -------------------------------------------------------------------------------- /src/lib/core/certinfo.c: -------------------------------------------------------------------------------- 1 | #include "certinfo.h" 2 | 3 | #ifdef WITH_TLS 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | struct PSC_CertInfo 10 | { 11 | X509 *cert; 12 | char *subject; 13 | char *fpstr; 14 | int havefingerprint; 15 | uint8_t fingerprint[64]; 16 | }; 17 | 18 | SOLOCAL PSC_CertInfo *PSC_CertInfo_create(X509 *cert) 19 | { 20 | PSC_CertInfo *self = PSC_malloc(sizeof *self); 21 | memset(self, 0, sizeof *self); 22 | self->cert = cert; 23 | return self; 24 | } 25 | 26 | SOEXPORT const uint8_t *PSC_CertInfo_fingerprint(const PSC_CertInfo *self) 27 | { 28 | if (!self->havefingerprint) 29 | { 30 | PSC_CertInfo *mut = (PSC_CertInfo *)self; 31 | X509_digest(self->cert, EVP_sha512(), mut->fingerprint, 0); 32 | mut->havefingerprint = 1; 33 | } 34 | return self->fingerprint; 35 | } 36 | 37 | SOEXPORT const char *PSC_CertInfo_fingerprintStr(const PSC_CertInfo *self) 38 | { 39 | if (!self->fpstr) 40 | { 41 | PSC_CertInfo *mut = (PSC_CertInfo *)self; 42 | mut->fpstr = PSC_malloc(129); 43 | char *fppos = mut->fpstr; 44 | const uint8_t *fp = PSC_CertInfo_fingerprint(self); 45 | for (int i = 0; i < 64; ++i) 46 | { 47 | sprintf(fppos, "%02hhx", fp[i]); 48 | fppos += 2; 49 | } 50 | } 51 | return self->fpstr; 52 | } 53 | 54 | SOEXPORT const char *PSC_CertInfo_subject(const PSC_CertInfo *self) 55 | { 56 | if (!self->subject) 57 | { 58 | PSC_CertInfo *mut = (PSC_CertInfo *)self; 59 | mut->subject = X509_NAME_oneline( 60 | X509_get_subject_name(self->cert), 0, 0); 61 | } 62 | return self->subject; 63 | } 64 | 65 | SOLOCAL void PSC_CertInfo_destroy(PSC_CertInfo *self) 66 | { 67 | if (!self) return; 68 | free(self->subject); 69 | free(self->fpstr); 70 | free(self); 71 | } 72 | 73 | #else 74 | #include 75 | 76 | SOEXPORT const uint8_t *PSC_CertInfo_fingerprint(const PSC_CertInfo *self) 77 | { 78 | (void)self; 79 | PSC_Service_panic("This version of libposercore does not support TLS!"); 80 | } 81 | 82 | SOEXPORT const char *PSC_CertInfo_subject(const PSC_CertInfo *self) 83 | { 84 | (void)self; 85 | PSC_Service_panic("This version of libposercore does not support TLS!"); 86 | } 87 | #endif 88 | -------------------------------------------------------------------------------- /src/lib/core/certinfo.h: -------------------------------------------------------------------------------- 1 | #ifndef POSER_CORE_INT_CERTINFO_H 2 | #define POSER_CORE_INT_CERTINFO_H 3 | 4 | #include 5 | 6 | #ifdef WITH_TLS 7 | #include 8 | 9 | PSC_CertInfo * 10 | PSC_CertInfo_create(X509 *cert) 11 | ATTR_RETNONNULL ATTR_NONNULL((1)); 12 | 13 | void 14 | PSC_CertInfo_destroy(PSC_CertInfo *self); 15 | 16 | #endif 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /src/lib/core/client.h: -------------------------------------------------------------------------------- 1 | #ifndef POSER_CORE_INT_CLIENT_H 2 | #define POSER_CORE_INT_CLIENT_H 3 | 4 | #include 5 | #include 6 | 7 | C_CLASS_DECL(PSC_IpAddr); 8 | 9 | #ifdef WITH_TLS 10 | void 11 | PSC_Connection_unreftlsctx(void); 12 | #endif 13 | 14 | void 15 | PSC_Connection_blacklistAddress(int hits, const PSC_IpAddr *addr) 16 | ATTR_NONNULL((2)); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /src/lib/core/connection.h: -------------------------------------------------------------------------------- 1 | #ifndef POSER_CORE_INT_CONNECTION_H 2 | #define POSER_CORE_INT_CONNECTION_H 3 | 4 | #include 5 | 6 | #include 7 | 8 | #ifdef WITH_TLS 9 | #include 10 | #include 11 | #endif 12 | 13 | #ifndef WRBUFSZ 14 | #define WRBUFSZ (16*1024) 15 | #endif 16 | 17 | #ifndef DEFRDBUFSZ 18 | #define DEFRDBUFSZ WRBUFSZ 19 | #endif 20 | 21 | C_CLASS_DECL(ConnectionPool); 22 | 23 | typedef enum ConnectionCreateMode 24 | { 25 | CCM_NORMAL, 26 | CCM_CONNECTING, 27 | CCM_PIPERD, 28 | CCM_PIPEWR 29 | } ConnectionCreateMode; 30 | 31 | typedef enum TlsMode 32 | { 33 | TM_NONE, 34 | TM_CLIENT, 35 | TM_SERVER 36 | } TlsMode; 37 | 38 | typedef struct ConnOpts 39 | { 40 | ConnectionPool *pool; 41 | size_t rdbufsz; 42 | #ifdef WITH_TLS 43 | SSL_CTX *tls_ctx; 44 | X509 *tls_cert; 45 | EVP_PKEY *tls_key; 46 | const char *tls_hostname; 47 | TlsMode tls_mode; 48 | int tls_noverify; 49 | #endif 50 | ConnectionCreateMode createmode; 51 | int blacklisthits; 52 | } ConnOpts; 53 | 54 | ConnectionPool * 55 | ConnectionPool_create(void); 56 | 57 | void 58 | ConnectionPool_destroy(ConnectionPool *self); 59 | 60 | PSC_Connection * 61 | PSC_Connection_create(int fd, const ConnOpts *opts) ATTR_NONNULL((2)); 62 | 63 | void 64 | PSC_Connection_setRemoteAddr(PSC_Connection *self, PSC_IpAddr *addr) 65 | CMETHOD; 66 | 67 | void 68 | PSC_Connection_setRemoteAddrStr(PSC_Connection *self, const char *addr) 69 | CMETHOD; 70 | 71 | void 72 | PSC_Connection_destroy(PSC_Connection *self); 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /src/lib/core/core.mk: -------------------------------------------------------------------------------- 1 | posercore_PRECHECK= ACCEPT4 ARC4R GETRANDOM MADVISE MADVFREE \ 2 | MANON MANONYMOUS MSTACK TLS_C11 TLS_GNU \ 3 | UCONTEXT XXHX86 4 | ACCEPT4_FUNC= accept4 5 | ACCEPT4_CFLAGS= -D_GNU_SOURCE 6 | ifneq ($(findstring -solaris,$(TARGETARCH)),) 7 | ACCEPT4_CFLAGS+= -Wno-macro-redefined -D_XOPEN_SOURCE=500 8 | endif 9 | ACCEPT4_HEADERS= sys/types.h sys/socket.h 10 | ACCEPT4_ARGS= int, struct sockaddr *, socklen_t *, int 11 | ARC4R_FUNC= arc4random_buf 12 | ARC4R_CFLAGS= -D_DEFAULT_SOURCE 13 | ARC4R_HEADERS= stdlib.h 14 | ARC4R_ARGS= void *, size_t 15 | ARC4R_RETURN= void 16 | BTINT_FUNC= backtrace_symbols_fd 17 | BTINT_HEADERS= execinfo.h 18 | BTINT_ARGS= void *const *, int, int 19 | BTSZT_FUNC= backtrace_symbols_fd 20 | BTSZT_HEADERS= execinfo.h 21 | BTSZT_ARGS= void *const *, size_t, int 22 | GETRANDOM_FUNC= getrandom 23 | GETRANDOM_HEADERS= sys/random.h 24 | GETRANDOM_RETURN= ssize_t 25 | GETRANDOM_ARGS= void *, size_t, unsigned 26 | MADVISE_FUNC= madvise 27 | MADVISE_CFLAGS= -D_DEFAULT_SOURCE 28 | MADVISE_HEADERS= sys/mman.h 29 | MADVISE_ARGS= void *, size_t, int 30 | MADVFREE_FLAG= MADV_FREE 31 | MADVFREE_CFLAGS= -D_DEFAULT_SOURCE 32 | MADVFREE_HEADERS= sys/mman.h 33 | MANON_FLAG= MAP_ANON 34 | MANON_CFLAGS= -D_DEFAULT_SOURCE 35 | MANON_HEADERS= sys/mman.h 36 | MANONYMOUS_FLAG= MAP_ANONYMOUS 37 | MANONYMOUS_CFLAGS= -D_DEFAULT_SOURCE 38 | MANONYMOUS_HEADERS= sys/mman.h 39 | MSTACK_FLAG= MAP_STACK 40 | MSTACK_CFLAGS= -D_DEFAULT_SOURCE 41 | MSTACK_HEADERS= sys/mman.h 42 | TLS_C11_TYPE= _Thread_local int 43 | TLS_GNU_TYPE= __thread int 44 | UCONTEXT_TYPE= ucontext_t 45 | UCONTEXT_HEADERS= ucontext.h 46 | XXHX86_FUNC= XXH_featureTest 47 | XXHX86_CFLAGS= -I./$(posercore_SRCDIR)/contrib/xxHash 48 | XXHX86_HEADERS= xxh_x86dispatch.c 49 | XXHX86_ARGS= void 50 | EVENTFD_FUNC= eventfd 51 | EVENTFD_HEADERS= sys/eventfd.h 52 | EVENTFD_ARGS= unsigned, int 53 | EVPORTS_FUNC= port_create 54 | EVPORTS_HEADERS= port.h 55 | EVPORTS_ARGS= void 56 | EPOLL_FUNC= epoll_pwait2 57 | EPOLL_HEADERS= sys/epoll.h 58 | EPOLL_ARGS= int, struct epoll_event [], int, \ 59 | const struct timespec *, const sigset_t * 60 | KQUEUE_FUNC= kqueue 61 | KQUEUE_HEADERS= sys/types.h sys/event.h sys/time.h 62 | KQUEUE_ARGS= void 63 | KQUEUEX_FUNC= kqueuex 64 | KQUEUEX_HEADERS= sys/types.h sys/event.h sys/time.h 65 | KQUEUEX_ARGS= u_int 66 | KQUEUE1_FUNC= kqueue1 67 | KQUEUE1_HEADERS= sys/types.h sys/event.h sys/time.h 68 | SIGNALFD_FUNC= signalfd 69 | SIGNALFD_HEADERS= sys/signalfd.h 70 | SIGNALFD_ARGS= int, const sigset_t *, int 71 | TIMERFD_FUNC= timerfd_create 72 | TIMERFD_HEADERS= sys/timerfd.h 73 | TIMERFD_ARGS= int, int 74 | 75 | ifeq ($(BUILDCFG),debug) 76 | posercore_PRECHECK+= BTINT BTSZT 77 | endif 78 | 79 | ifneq ($(WITHOUT_EVENTFD),1) 80 | posercore_PRECHECK+= EVENTFD 81 | endif 82 | 83 | ifneq ($(WITHOUT_EVPORTS),1) 84 | posercore_PRECHECK+= EVPORTS 85 | endif 86 | 87 | ifneq ($(WITHOUT_EPOLL),1) 88 | posercore_PRECHECK+= EPOLL 89 | endif 90 | 91 | ifneq ($(WITHOUT_KQUEUE),1) 92 | posercore_PRECHECK+= KQUEUE KQUEUEX KQUEUE1 93 | endif 94 | 95 | ifneq ($(WITHOUT_SIGNALFD),1) 96 | posercore_PRECHECK+= SIGNALFD 97 | endif 98 | 99 | ifneq ($(WITHOUT_TIMERFD),1) 100 | posercore_PRECHECK+= TIMERFD 101 | endif 102 | 103 | posercore_MODULES= base64 \ 104 | certinfo \ 105 | client \ 106 | connection \ 107 | daemon \ 108 | dictionary \ 109 | event \ 110 | hash \ 111 | hashtable \ 112 | ipaddr \ 113 | json \ 114 | list \ 115 | log \ 116 | process \ 117 | queue \ 118 | random \ 119 | ratelimit \ 120 | resolver \ 121 | runopts \ 122 | server \ 123 | service \ 124 | sharedobj \ 125 | $(if $(filter 1,$(posercore_HAVE_UCONTEXT)), \ 126 | stackmgr) \ 127 | stringbuilder \ 128 | threadpool \ 129 | timer \ 130 | util \ 131 | xxhash \ 132 | xxhx86 133 | 134 | posercore_HEADERS_INSTALL= core \ 135 | core/base64 \ 136 | core/certinfo \ 137 | core/client \ 138 | core/connection \ 139 | core/daemon \ 140 | core/dictionary \ 141 | core/event \ 142 | core/hash \ 143 | core/hashtable \ 144 | core/ipaddr \ 145 | core/json \ 146 | core/list \ 147 | core/log \ 148 | core/process \ 149 | core/proto \ 150 | core/queue \ 151 | core/random \ 152 | core/ratelimit \ 153 | core/resolver \ 154 | core/runopts \ 155 | core/server \ 156 | core/service \ 157 | core/stringbuilder \ 158 | core/threadpool \ 159 | core/timer \ 160 | core/util \ 161 | decl 162 | 163 | posercore_PRECFLAGS?= -I.$(PSEP)include 164 | posercore_DEFINES= # 165 | posercore_LDFLAGS= -pthread 166 | posercore_HEADERDIR= include$(PSEP)poser 167 | posercore_HEADERTGTDIR= $(includedir)$(PSEP)poser 168 | posercore_VERSION= 2.0.0 169 | 170 | ifneq ($(findstring -solaris,$(TARGETARCH)),) 171 | posercore_PRECFLAGS+= -D__EXTENSIONS__ 172 | posercore_LIBS+= socket 173 | endif 174 | 175 | ifneq ($(WITH_ATOMICS),1) 176 | posercore_DEFINES+= -DNO_ATOMICS 177 | endif 178 | 179 | ifeq ($(WITH_POLL),1) 180 | posercore_DEFINES+= -DWITH_POLL 181 | endif 182 | 183 | ifeq ($(WITH_TLS),1) 184 | ifneq ($(OPENSSLINC)$(OPENSSLLIB),) 185 | ifeq ($(OPENSSLINC),) 186 | $(error OPENSSLLIB specified without OPENSSLINC) 187 | endif 188 | ifeq ($(OPENSSLLIB),) 189 | $(error OPENSSLINC specified without OPENSSLLIB) 190 | endif 191 | posercore_INCLUDES+= -I$(OPENSSLINC) 192 | posercore_LDFLAGS+= -L$(OPENSSLLIB) 193 | posercore_LIBS+= ssl crypto 194 | else 195 | posercore_PKGDEPS+= openssl >= 1.1 196 | endif 197 | posercore_DEFINES+= -DWITH_TLS 198 | endif 199 | 200 | $(call librules, posercore) 201 | 202 | ifeq ($(posercore_HAVE_BTSZT),1) 203 | posercore_LIBS+= execinfo 204 | endif 205 | 206 | ifeq ($(posercore_HAVE_TLS_C11),1) 207 | posercore_DEFINES+= -DTHREADLOCAL=_Thread_local 208 | else 209 | ifeq ($(posercore_HAVE_TLS_GNU),1) 210 | posercore_DEFINES+= -DTHREADLOCAL=__thread 211 | else 212 | $(error No thread-local-storage suppport detected, cannot build poser) 213 | endif 214 | endif 215 | 216 | ifneq ($(WITH_POLL),1) 217 | ifneq ($(posercore_HAVE_EPOLL),1) 218 | ifneq ($(posercore_HAVE_KQUEUE),1) 219 | posercore_DEFINES+= -DFD_SETSIZE=$(FD_SETSIZE) 220 | endif 221 | endif 222 | endif 223 | 224 | ifeq ($(WITH_EVENTFD),1) 225 | ifeq ($(WITHOUT_EVENTFD),1) 226 | $(error Cannot set both WITH_EVENTFD and WITHOUT_EVENTFD) 227 | endif 228 | ifneq ($(posercore_HAVE_EVENTFD),1) 229 | $(error Requested eventfd (WITH_EVENTFD), but not found) 230 | endif 231 | endif 232 | 233 | ifeq ($(WITH_EVPORTS),1) 234 | ifeq ($(WITHOUT_EVPORTS),1) 235 | $(error Cannot set both WITH_EVPORTS and WITHOUT_EVPORTS) 236 | endif 237 | ifeq ($(WITH_POLL),1) 238 | $(error Cannot set both WITH_EVPORTS and WITH_POLL) 239 | endif 240 | ifneq ($(posercore_HAVE_EVPORTS),1) 241 | $(error Requested event ports (WITH_EVPORTS), but not found) 242 | endif 243 | endif 244 | 245 | ifeq ($(WITH_EPOLL),1) 246 | ifeq ($(WITHOUT_EPOLL),1) 247 | $(error Cannot set both WITH_EPOLL and WITHOUT_EPOLL) 248 | endif 249 | ifeq ($(WITH_EVPORTS),1) 250 | $(error Cannot set both WITH_EVPORTS and WITH_EPOLL) 251 | endif 252 | ifeq ($(WITH_POLL),1) 253 | $(error Cannot set both WITH_POLL and WITH_EPOLL) 254 | endif 255 | ifneq ($(posercore_HAVE_EPOLL),1) 256 | $(error Requested epoll (WITH_EPOLL), but not found) 257 | endif 258 | endif 259 | 260 | ifeq ($(WITH_KQUEUE),1) 261 | ifeq ($(WITHOUT_KQUEUE),1) 262 | $(error Cannot set both WITH_KQUEUE and WITHOUT_KQUEUE) 263 | endif 264 | ifeq ($(WITH_EPOLL),1) 265 | $(error Cannot set both WITH_EPOLL and WITH_KQUEUE) 266 | endif 267 | ifeq ($(WITH_EVPORTS),1) 268 | $(error Cannot set both WITH_EVPORTS and WITH_KQUEUE) 269 | endif 270 | ifeq ($(WITH_POLL),1) 271 | $(error Cannot set both WITH_POLL and WITH_KQUEUE) 272 | endif 273 | ifneq ($(posercore_HAVE_KQUEUE),1) 274 | $(error Requested kqueue (WITH_KQUEUE), but not found) 275 | endif 276 | endif 277 | 278 | ifeq ($(WITH_SIGNALFD),1) 279 | ifeq ($(WITHOUT_SIGNALFD),1) 280 | $(error Cannot set both WITH_SIGNALFD and WITHOUT_SIGNALFD) 281 | endif 282 | ifneq ($(posercore_HAVE_SIGNALFD),1) 283 | $(error Requested signalfd (WITH_SIGNALFD), but not found) 284 | endif 285 | endif 286 | 287 | ifeq ($(WITH_TIMERFD),1) 288 | ifeq ($(WITHOUT_TIMERFD),1) 289 | $(error Cannot set both WITH_TIMERFD and WITHOUT_TIMERFD) 290 | endif 291 | ifneq ($(posercore_HAVE_TIMERFD),1) 292 | $(error Requested timerfd (WITH_TIMERFD), but not found) 293 | endif 294 | endif 295 | 296 | ifneq ($(posercore_HAVE_ACCEPT4),1) 297 | define posercore_warn_accept4 298 | **WARNING** 299 | 300 | GNU-style accept4() API not detected, falling back to non-atomic 301 | configuration of socket file descriptors. 302 | 303 | This introduces a small risk to leak sockets to child processes! 304 | 305 | endef 306 | $(warning $(posercore_warn_accept4)) 307 | endif 308 | 309 | ifneq ($(posercore_HAVE_UCONTEXT),1) 310 | define posercore_warn_ucontext 311 | **WARNING** 312 | 313 | User context switching () not detected. Awaiting a PSC_AsyncTask 314 | will therefore block the thread doing this until the task is completed. 315 | 316 | This might affect performance of applications using this feature. 317 | 318 | endef 319 | $(warning $(posercore_warn_ucontext)) 320 | endif 321 | -------------------------------------------------------------------------------- /src/lib/core/daemon.c: -------------------------------------------------------------------------------- 1 | #define _DEFAULT_SOURCE 2 | 3 | #include "runopts.h" 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | static FILE *openpidfile(const char *pidfile) ATTR_NONNULL((1)); 18 | static int waitpflock(FILE *pf, const char *pidfile) 19 | ATTR_NONNULL((1)) ATTR_NONNULL((2)); 20 | 21 | static FILE *openpidfile(const char *pidfile) 22 | { 23 | struct flock pflock; 24 | pid_t pid; 25 | 26 | FILE *pf = fopen(pidfile, "r"); 27 | if (pf) 28 | { 29 | memset(&pflock, 0, sizeof pflock); 30 | pflock.l_type = F_RDLCK; 31 | pflock.l_whence = SEEK_SET; 32 | if (fcntl(fileno(pf), F_GETLK, &pflock) < 0) 33 | { 34 | PSC_Log_errfmt(PSC_L_ERROR, "error getting lock info on `%s'", 35 | pidfile); 36 | return 0; 37 | } 38 | int locked = (pflock.l_type != F_UNLCK); 39 | if (!locked) 40 | { 41 | struct flock pfrdlock; 42 | memset(&pfrdlock, 0, sizeof pfrdlock); 43 | pfrdlock.l_type = F_RDLCK; 44 | pfrdlock.l_whence = SEEK_SET; 45 | if (fcntl(fileno(pf), F_SETLK, &pfrdlock) < 0) 46 | { 47 | locked = 1; 48 | memset(&pflock, 0, sizeof pflock); 49 | pflock.l_type = F_RDLCK; 50 | pflock.l_whence = SEEK_SET; 51 | fcntl(fileno(pf), F_GETLK, &pflock); 52 | } 53 | else 54 | { 55 | PSC_Log_fmt(PSC_L_WARNING, "removing stale pidfile `%s'", 56 | pidfile); 57 | if (unlink(pidfile) < 0) 58 | { 59 | PSC_Log_errfmt(PSC_L_ERROR, "cannot remove `%s'", pidfile); 60 | return 0; 61 | } 62 | fclose(pf); 63 | } 64 | } 65 | if (locked) 66 | { 67 | int prc = fscanf(pf, "%d", &pid); 68 | fclose(pf); 69 | if (prc < 1 || pid != pflock.l_pid) 70 | { 71 | if (prc < 1) pid = -1; 72 | PSC_Log_fmt(PSC_L_ERROR, "pidfile `%s' content (pid %d) and " 73 | "lock owner (pid %d) disagree! This should never " 74 | "happen, giving up!", pidfile, pid, pflock.l_pid); 75 | } 76 | else 77 | { 78 | PSC_Log_fmt(PSC_L_ERROR, "daemon already running with pid %d", 79 | pid); 80 | } 81 | return 0; 82 | } 83 | } 84 | pf = fopen(pidfile, "w"); 85 | if (!pf) 86 | { 87 | PSC_Log_errfmt(PSC_L_ERROR, "cannot open pidfile `%s' for writing", 88 | pidfile); 89 | return 0; 90 | } 91 | memset(&pflock, 0, sizeof pflock); 92 | pflock.l_type = F_WRLCK; 93 | pflock.l_whence = SEEK_SET; 94 | if (fcntl(fileno(pf), F_SETLK, &pflock) < 0) 95 | { 96 | fclose(pf); 97 | PSC_Log_errfmt(PSC_L_ERROR, 98 | "locking own pidfile `%s' failed", pidfile); 99 | return 0; 100 | } 101 | 102 | return pf; 103 | } 104 | 105 | static int waitpflock(FILE *pf, const char *pidfile) 106 | { 107 | struct flock pflock; 108 | int lrc; 109 | 110 | memset(&pflock, 0, sizeof pflock); 111 | pflock.l_type = F_WRLCK; 112 | pflock.l_whence = SEEK_SET; 113 | do 114 | { 115 | errno = 0; 116 | lrc = fcntl(fileno(pf), F_SETLKW, &pflock); 117 | } while (lrc < 0 && errno == EINTR); 118 | if (lrc < 0) 119 | { 120 | PSC_Log_errfmt(PSC_L_ERROR, 121 | "locking own pidfile `%s' failed", pidfile); 122 | return -1; 123 | } 124 | return 0; 125 | } 126 | 127 | SOEXPORT int PSC_Daemon_run(PSC_Daemon_main dmain, void *data) 128 | { 129 | pid_t pid, sid; 130 | int rc = EXIT_FAILURE; 131 | FILE *pf = 0; 132 | 133 | PSC_RunOpts *opts = runOpts(); 134 | if (!opts->daemonize) return dmain(data); 135 | 136 | if (opts->pidfile && !(pf = openpidfile(opts->pidfile))) goto done; 137 | 138 | int pfd[2]; 139 | if (!opts->waitLaunched || pipe(pfd) < 0) 140 | { 141 | pfd[0] = -1; 142 | pfd[1] = -1; 143 | } 144 | 145 | pid = fork(); 146 | 147 | if (pid < 0) 148 | { 149 | if (pfd[0] >= 0) close(pfd[0]); 150 | if (pfd[1] >= 0) close(pfd[1]); 151 | PSC_Log_err(PSC_L_ERROR, "failed to fork (1)"); 152 | goto done; 153 | } 154 | 155 | if (pid > 0) 156 | { 157 | if (pfd[1] >= 0) close(pfd[1]); 158 | if (pf) fclose(pf); 159 | int drc = EXIT_SUCCESS; 160 | if (pfd[0] >= 0) 161 | { 162 | char buf[256]; 163 | ssize_t sz; 164 | ssize_t wrc; 165 | size_t bp; 166 | while ((sz = read(pfd[0], buf, sizeof buf)) > 0) 167 | { 168 | bp = 0; 169 | if (!buf[sz-1]) 170 | { 171 | drc = EXIT_FAILURE; 172 | if (sz > 1) do 173 | { 174 | wrc = write(STDERR_FILENO, buf+bp, sz-bp-1); 175 | } while (wrc >= 0 && (bp += wrc) < (size_t)sz - 1); 176 | break; 177 | } 178 | do 179 | { 180 | wrc = write(STDERR_FILENO, buf+bp, sz-bp); 181 | } while (wrc >= 0 && (bp += wrc) < (size_t)sz); 182 | } 183 | close(pfd[0]); 184 | } 185 | return drc; 186 | } 187 | 188 | if (pf && waitpflock(pf, opts->pidfile) < 0) goto done; 189 | 190 | if (pfd[0] >= 0) close(pfd[0]); 191 | 192 | sid = setsid(); 193 | if (sid < 0) 194 | { 195 | PSC_Log_err(PSC_L_ERROR, "setsid() failed"); 196 | goto done; 197 | } 198 | 199 | struct sigaction handler; 200 | memset(&handler, 0, sizeof handler); 201 | handler.sa_handler = SIG_IGN; 202 | sigemptyset(&handler.sa_mask); 203 | sigaction(SIGTERM, &handler, 0); 204 | sigaction(SIGINT, &handler, 0); 205 | sigaction(SIGHUP, &handler, 0); 206 | sigaction(SIGPIPE, &handler, 0); 207 | sigaction(SIGUSR1, &handler, 0); 208 | 209 | pid = fork(); 210 | 211 | if (pid < 0) 212 | { 213 | PSC_Log_err(PSC_L_ERROR, "failed to fork (2)"); 214 | goto done; 215 | } 216 | 217 | if (pid > 0) 218 | { 219 | if (pf) 220 | { 221 | fprintf(pf, "%d\n", pid); 222 | fclose(pf); 223 | } 224 | return EXIT_SUCCESS; 225 | } 226 | 227 | if (pf && waitpflock(pf, opts->pidfile) < 0) goto done; 228 | 229 | if (chdir("/") < 0) 230 | { 231 | PSC_Log_err(PSC_L_ERROR, "chdir(\"/\") failed"); 232 | goto done; 233 | } 234 | 235 | umask(0); 236 | 237 | int nullfd = open("/dev/null", O_RDWR); 238 | if (nullfd < 0) 239 | { 240 | close(STDOUT_FILENO); 241 | if (pfd[1] < 0) close(STDERR_FILENO); 242 | } 243 | else 244 | { 245 | dup2(nullfd, STDIN_FILENO); 246 | dup2(nullfd, STDOUT_FILENO); 247 | if (pfd[1] < 0) dup2(nullfd, STDERR_FILENO); 248 | close(nullfd); 249 | } 250 | if (pfd[1] >= 0) 251 | { 252 | dup2(pfd[1], STDERR_FILENO); 253 | close(pfd[1]); 254 | } 255 | 256 | PSC_Log_msg(PSC_L_INFO, "forked into background"); 257 | rc = dmain(data); 258 | if (rc != EXIT_SUCCESS && write(STDERR_FILENO, "\0", 1) < 1) 259 | { 260 | PSC_Log_err(PSC_L_WARNING, "daemon: cannot notify parent process"); 261 | } 262 | if (pf) 263 | { 264 | fclose(pf); 265 | pf = 0; 266 | } 267 | if (opts->pidfile) unlink(opts->pidfile); 268 | 269 | done: 270 | if (pf) fclose(pf); 271 | return rc; 272 | } 273 | 274 | SOEXPORT void PSC_Daemon_launched(void) 275 | { 276 | dup2(STDIN_FILENO, STDERR_FILENO); 277 | } 278 | 279 | -------------------------------------------------------------------------------- /src/lib/core/event.c: -------------------------------------------------------------------------------- 1 | #include "event.h" 2 | 3 | #include "assert.h" 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #ifndef NDEBUG 12 | #include 13 | #endif 14 | 15 | typedef struct EvHandler 16 | { 17 | PSC_EventHandler cb; 18 | void *receiver; 19 | int id; 20 | } EvHandler; 21 | 22 | /* Explicit initializer, use to avoid non-zero padding, so the struct 23 | * can be compared byte-wise */ 24 | #define EVHDL_INIT(x, c, r, i) do { \ 25 | memset((x), 0, sizeof *(x)); \ 26 | (x)->cb = (c); \ 27 | (x)->receiver = (r); \ 28 | (x)->id = (i); \ 29 | } while (0); 30 | 31 | struct EvHandlerEntry 32 | { 33 | EvHandlerEntry *next; 34 | EvHandlerEntry *prev; 35 | EvHandler handler; 36 | }; 37 | 38 | struct EvHandlerPool 39 | { 40 | EvHandlerEntry *first; 41 | EvHandlerEntry *last; 42 | size_t refcnt; 43 | #ifndef NDEBUG 44 | void *thr; 45 | #endif 46 | }; 47 | 48 | static THREADLOCAL EvHandlerPool *pool; 49 | 50 | static EvHandlerPool *EvHandlerPool_init(void) 51 | { 52 | if (!pool) 53 | { 54 | pool = PSC_malloc(sizeof *pool); 55 | memset(pool, 0, sizeof *pool); 56 | #ifndef NDEBUG 57 | pool->thr = (void *)pthread_self(); 58 | #endif 59 | } 60 | ++pool->refcnt; 61 | return pool; 62 | } 63 | 64 | static void EvHandlerPool_done(EvHandlerPool *self) 65 | { 66 | if (!--self->refcnt) 67 | { 68 | pool = 0; 69 | for (EvHandlerEntry *e = self->first, *n = 0; e; e = n) 70 | { 71 | n = e->next; 72 | free(e); 73 | } 74 | free(self); 75 | } 76 | } 77 | 78 | static EvHandlerEntry *EvHandlerPool_get(EvHandlerPool *self) 79 | { 80 | assert(self->thr == (void *)pthread_self()); 81 | EvHandlerEntry *e = self->first; 82 | if (e) 83 | { 84 | if (!(self->first = e->next)) self->last = 0; 85 | } 86 | else e = PSC_malloc(sizeof *e); 87 | return e; 88 | } 89 | 90 | static void EvHandlerPool_return(EvHandlerPool *self, EvHandlerEntry *e) 91 | { 92 | assert(self->thr == (void *)pthread_self()); 93 | e->next = 0; 94 | if (self->last) self->last->next = e; 95 | else self->first = e; 96 | self->last = e; 97 | } 98 | 99 | SOLOCAL void PSC_Event_initStatic(PSC_Event *self, void *sender) 100 | { 101 | memset(self, 0, sizeof *self); 102 | self->sender = sender; 103 | self->pool = EvHandlerPool_init(); 104 | } 105 | 106 | SOEXPORT PSC_Event *PSC_Event_create(void *sender) 107 | { 108 | assert(PSC_Service_threadNo() > -2); 109 | PSC_Event *self = PSC_malloc(sizeof *self); 110 | PSC_Event_initStatic(self, sender); 111 | return self; 112 | } 113 | 114 | static EvHandlerEntry *findEntry(PSC_Event *self, EvHandler *handler) 115 | { 116 | for (EvHandlerEntry *e = self->first; e; e = e->next) 117 | { 118 | if (!memcmp(&e->handler, handler, sizeof *handler)) return e; 119 | } 120 | return 0; 121 | } 122 | 123 | SOEXPORT void PSC_Event_register(PSC_Event *self, void *receiver, 124 | PSC_EventHandler handler, int id) 125 | { 126 | EvHandler hdl; 127 | EVHDL_INIT(&hdl, handler, receiver, id); 128 | if (findEntry(self, &hdl)) return; 129 | EvHandlerEntry *entry = EvHandlerPool_get(self->pool); 130 | entry->prev = self->last; 131 | entry->next = 0; 132 | entry->handler = hdl; 133 | if (self->last) self->last->next = entry; 134 | else self->first = entry; 135 | self->last = entry; 136 | } 137 | 138 | SOEXPORT void PSC_Event_unregister( 139 | PSC_Event *self, void *receiver, PSC_EventHandler handler, int id) 140 | { 141 | if (!self->first) return; 142 | EvHandler hdl; 143 | EVHDL_INIT(&hdl, handler, receiver, id); 144 | EvHandlerEntry *entry = findEntry(self, &hdl); 145 | if (!entry) return; 146 | if (entry == self->handling) self->handling = entry->next; 147 | if (entry->prev) entry->prev->next = entry->next; 148 | else self->first = entry->next; 149 | if (entry->next) entry->next->prev = entry->prev; 150 | else self->last = entry->prev; 151 | EvHandlerPool_return(self->pool, entry); 152 | } 153 | 154 | SOEXPORT void PSC_Event_raise(PSC_Event *self, int id, void *args) 155 | { 156 | assert(self->pool->thr == (void *)pthread_self()); 157 | for (EvHandlerEntry *entry = self->first; entry;) 158 | { 159 | self->handling = entry->next; 160 | if (entry->handler.id == id) 161 | { 162 | if (!args && id) args = &id; 163 | entry->handler.cb(entry->handler.receiver, self->sender, args); 164 | } 165 | entry = self->handling; 166 | } 167 | } 168 | 169 | SOLOCAL void PSC_Event_destroyStatic(PSC_Event *self) 170 | { 171 | if (!self->pool) return; 172 | for (EvHandlerEntry *entry = self->first; entry;) 173 | { 174 | EvHandlerEntry *next = entry->next; 175 | EvHandlerPool_return(self->pool, entry); 176 | entry = next; 177 | } 178 | EvHandlerPool_done(self->pool); 179 | memset(self, 0, sizeof *self); 180 | } 181 | 182 | SOEXPORT void PSC_Event_destroy(PSC_Event *self) 183 | { 184 | if (!self) return; 185 | for (EvHandlerEntry *entry = self->first; entry;) 186 | { 187 | EvHandlerEntry *next = entry->next; 188 | EvHandlerPool_return(self->pool, entry); 189 | entry = next; 190 | } 191 | EvHandlerPool_done(self->pool); 192 | free(self); 193 | } 194 | 195 | -------------------------------------------------------------------------------- /src/lib/core/event.h: -------------------------------------------------------------------------------- 1 | #ifndef POSER_CORE_INT_EVENT_H 2 | #define POSER_CORE_INT_EVENT_H 3 | 4 | #include 5 | 6 | #include 7 | 8 | C_CLASS_DECL(EvHandlerEntry); 9 | C_CLASS_DECL(EvHandlerPool); 10 | 11 | struct PSC_Event 12 | { 13 | void *sender; 14 | EvHandlerPool *pool; 15 | EvHandlerEntry *first; 16 | EvHandlerEntry *last; 17 | EvHandlerEntry *handling; 18 | }; 19 | 20 | void PSC_Event_initStatic(PSC_Event *self, void *sender) CMETHOD; 21 | void PSC_Event_destroyStatic(PSC_Event *self); 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /src/lib/core/hash.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "xxhash.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define SECRETSIZE 192 12 | 13 | struct PSC_Hash 14 | { 15 | uint8_t *secret; 16 | }; 17 | 18 | static uint8_t *getSecret(void) 19 | { 20 | static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; 21 | static int haveSecret = 0; 22 | static uint8_t secret[SECRETSIZE]; 23 | 24 | uint8_t *s = 0; 25 | 26 | pthread_mutex_lock(&lock); 27 | if (haveSecret) s = secret; 28 | else 29 | { 30 | if (PSC_Random_bytes(secret, SECRETSIZE, PSC_RF_NONBLOCK) 31 | == SECRETSIZE) 32 | { 33 | s = secret; 34 | haveSecret = 1; 35 | } 36 | } 37 | pthread_mutex_unlock(&lock); 38 | return s; 39 | } 40 | 41 | SOEXPORT PSC_Hash *PSC_Hash_create(int func, int flags) 42 | { 43 | (void)func; 44 | 45 | uint8_t *secret = 0; 46 | if (flags && !(secret = getSecret())) return 0; 47 | 48 | PSC_Hash *self = PSC_malloc(sizeof *self); 49 | self->secret = secret; 50 | return self; 51 | } 52 | 53 | SOEXPORT uint64_t PSC_Hash_bytes(PSC_Hash *self, 54 | const void *data, size_t size) 55 | { 56 | return self->secret 57 | ? XXH3_64bits_withSecret(data, size, self->secret, SECRETSIZE) 58 | : XXH3_64bits(data, size); 59 | } 60 | 61 | SOEXPORT uint64_t PSC_Hash_string(PSC_Hash *self, const char *str) 62 | { 63 | return PSC_Hash_bytes(self, (const uint8_t *)str, strlen(str)); 64 | } 65 | 66 | SOEXPORT void PSC_Hash_destroy(PSC_Hash *self) 67 | { 68 | free(self); 69 | } 70 | 71 | -------------------------------------------------------------------------------- /src/lib/core/hashtable.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | typedef struct PSC_HashTableEntry PSC_HashTableEntry; 11 | struct PSC_HashTableEntry 12 | { 13 | PSC_HashTableEntry *next; 14 | char *key; 15 | void *obj; 16 | void (*deleter)(void *); 17 | }; 18 | 19 | struct PSC_HashTable 20 | { 21 | PSC_Hash *hash; 22 | size_t count; 23 | size_t mask; 24 | uint8_t bits; 25 | PSC_HashTableEntry *bucket[]; 26 | }; 27 | 28 | typedef struct IteratorEntry 29 | { 30 | const char *key; 31 | void *obj; 32 | } IteratorEntry; 33 | 34 | struct PSC_HashTableIterator 35 | { 36 | size_t count; 37 | size_t pos; 38 | IteratorEntry entries[]; 39 | }; 40 | 41 | static size_t hashstr(const PSC_HashTable *self, const char *key) 42 | { 43 | uint64_t hash = PSC_Hash_string(self->hash, key); 44 | return hash & self->mask; 45 | } 46 | 47 | SOEXPORT PSC_HashTable *PSC_HashTable_create(uint8_t bits) 48 | { 49 | if (bits < 2) bits = 2; 50 | else if (bits > 16) bits = 16; 51 | size_t htsize = (1U << bits); 52 | PSC_HashTable *self = PSC_malloc( 53 | sizeof *self + htsize * sizeof *self->bucket); 54 | self->hash = PSC_Hash_create(0, 0); 55 | self->count = 0; 56 | self->mask = htsize - 1; 57 | self->bits = bits; 58 | memset(self->bucket, 0, htsize * sizeof *self->bucket); 59 | return self; 60 | } 61 | 62 | SOEXPORT void PSC_HashTable_set(PSC_HashTable *self, const char *key, 63 | void *obj, void (*deleter)(void *)) 64 | { 65 | size_t h = hashstr(self, key); 66 | PSC_HashTableEntry *parent = 0; 67 | PSC_HashTableEntry *entry = self->bucket[h]; 68 | while (entry) 69 | { 70 | if (!strcmp(entry->key, key)) break; 71 | parent = entry; 72 | entry = parent->next; 73 | } 74 | if (entry) 75 | { 76 | if (entry->deleter) entry->deleter(entry->obj); 77 | entry->obj = obj; 78 | entry->deleter = deleter; 79 | } 80 | else 81 | { 82 | entry = PSC_malloc(sizeof *entry); 83 | entry->next = 0; 84 | entry->key = PSC_copystr(key); 85 | entry->obj = obj; 86 | entry->deleter = deleter; 87 | if (parent) parent->next = entry; 88 | else self->bucket[h] = entry; 89 | ++self->count; 90 | } 91 | } 92 | 93 | SOEXPORT int PSC_HashTable_delete(PSC_HashTable *self, const char *key) 94 | { 95 | size_t h = hashstr(self, key); 96 | PSC_HashTableEntry *parent = 0; 97 | PSC_HashTableEntry *entry = self->bucket[h]; 98 | while (entry) 99 | { 100 | if (!strcmp(entry->key, key)) break; 101 | parent = entry; 102 | entry = entry->next; 103 | } 104 | if (entry) 105 | { 106 | if (entry->deleter) entry->deleter(entry->obj); 107 | if (parent) parent->next = entry->next; 108 | else self->bucket[h] = entry->next; 109 | free(entry->key); 110 | free(entry); 111 | --self->count; 112 | return 1; 113 | } 114 | return 0; 115 | } 116 | 117 | SOEXPORT int PSC_HashTable_deleteAll(PSC_HashTable *self, 118 | int (*matcher)(const char *, void *, const void *), const void *arg) 119 | { 120 | int deleted = 0; 121 | for (size_t h = 0; h <= self->mask; ++h) 122 | { 123 | PSC_HashTableEntry *parent = 0; 124 | PSC_HashTableEntry *entry = self->bucket[h]; 125 | while (entry) 126 | { 127 | PSC_HashTableEntry *next = entry->next; 128 | if (matcher(entry->key, entry->obj, arg)) 129 | { 130 | if (entry->deleter) entry->deleter(entry->obj); 131 | if (parent) parent->next = next; 132 | else self->bucket[h] = next; 133 | free(entry->key); 134 | free(entry); 135 | --self->count; 136 | ++deleted; 137 | } 138 | else parent = entry; 139 | entry = next; 140 | } 141 | } 142 | return deleted; 143 | } 144 | 145 | SOEXPORT size_t PSC_HashTable_count(const PSC_HashTable *self) 146 | { 147 | return self->count; 148 | } 149 | 150 | SOEXPORT void *PSC_HashTable_get(const PSC_HashTable *self, const char *key) 151 | { 152 | PSC_HashTableEntry *entry = self->bucket[hashstr(self, key)]; 153 | while (entry) 154 | { 155 | if (!strcmp(entry->key, key)) return entry->obj; 156 | entry = entry->next; 157 | } 158 | return 0; 159 | } 160 | 161 | SOEXPORT PSC_HashTableIterator *PSC_HashTable_iterator(const PSC_HashTable *self) 162 | { 163 | PSC_HashTableIterator *iter = PSC_malloc( 164 | sizeof *iter + self->count * sizeof *iter->entries); 165 | iter->count = self->count; 166 | iter->pos = self->count; 167 | size_t pos = 0; 168 | for (size_t h = 0; h <= self->mask; ++h) 169 | { 170 | PSC_HashTableEntry *entry = self->bucket[h]; 171 | while (entry) 172 | { 173 | iter->entries[pos].key = entry->key; 174 | iter->entries[pos].obj = entry->obj; 175 | ++pos; 176 | entry = entry->next; 177 | } 178 | } 179 | return iter; 180 | } 181 | 182 | SOEXPORT void PSC_HashTable_destroy(PSC_HashTable *self) 183 | { 184 | if (!self) return; 185 | for (size_t h = 0; h <= self->mask; ++h) 186 | { 187 | PSC_HashTableEntry *entry = self->bucket[h]; 188 | PSC_HashTableEntry *next; 189 | while (entry) 190 | { 191 | next = entry->next; 192 | if (entry->deleter) entry->deleter(entry->obj); 193 | free(entry->key); 194 | free(entry); 195 | entry = next; 196 | } 197 | } 198 | PSC_Hash_destroy(self->hash); 199 | free(self); 200 | } 201 | 202 | SOEXPORT int PSC_HashTableIterator_moveNext(PSC_HashTableIterator *self) 203 | { 204 | if (self->pos >= self->count) self->pos = 0; 205 | else ++self->pos; 206 | return self->pos < self->count; 207 | } 208 | 209 | SOEXPORT const char *PSC_HashTableIterator_key( 210 | const PSC_HashTableIterator *self) 211 | { 212 | if (self->pos >= self->count) return 0; 213 | return self->entries[self->pos].key; 214 | } 215 | 216 | SOEXPORT void *PSC_HashTableIterator_current(const PSC_HashTableIterator *self) 217 | { 218 | if (self->pos >= self->count) return 0; 219 | return self->entries[self->pos].obj; 220 | } 221 | 222 | SOEXPORT void PSC_HashTableIterator_destroy(PSC_HashTableIterator *self) 223 | { 224 | free(self); 225 | } 226 | 227 | -------------------------------------------------------------------------------- /src/lib/core/ipaddr.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 200112L 2 | 3 | #include "ipaddr.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #undef IPA_NO_ATOMICS 16 | #if defined(NO_ATOMICS) || defined (__STDC_NO_ATOMICS__) 17 | # define IPA_NO_ATOMICS 18 | #else 19 | # include 20 | # if ATOMIC_INT_LOCK_FREE != 2 21 | # define IPA_NO_ATOMICS 22 | # endif 23 | #endif 24 | 25 | #ifdef IPA_NO_ATOMICS 26 | # include 27 | #endif 28 | 29 | struct PSC_IpAddr 30 | { 31 | #ifdef IPA_NO_ATOMICS 32 | pthread_mutex_t reflock; 33 | unsigned refcnt; 34 | #else 35 | atomic_uint refcnt; 36 | #endif 37 | PSC_Proto proto; 38 | unsigned prefixlen; 39 | int port; 40 | uint8_t data[16]; 41 | char str[44]; 42 | }; 43 | #define IPADDR_CLONEOFFSET offsetof(PSC_IpAddr, proto) 44 | 45 | static int parsev4(uint8_t *data, const char *buf) 46 | { 47 | uint8_t v4dat[4] = { 0 }; 48 | const char *bufp = buf; 49 | for (int i = 0; i < 4; ++i) 50 | { 51 | errno = 0; 52 | char *endp = 0; 53 | long byte = strtol(bufp, &endp, 10); 54 | if (!endp || endp == bufp || (i == 3 ? *endp : *endp != '.') 55 | || errno == ERANGE || byte < 0 || byte > 255) return -1; 56 | v4dat[i] = byte; 57 | bufp = endp + (i < 3); 58 | } 59 | memcpy(data + 12, v4dat, 4); 60 | return 0; 61 | } 62 | 63 | static int parsev6(uint8_t *data, const char *buf) 64 | { 65 | uint8_t taildat[16] = { 0 }; 66 | size_t len = 0; 67 | int taillen = -1; 68 | 69 | while (*buf) 70 | { 71 | if (taillen < 0 && buf[0] == ':' && buf[1] == ':') 72 | { 73 | taillen = 0; 74 | buf += 2; 75 | continue; 76 | } 77 | 78 | errno = 0; 79 | char *endp = 0; 80 | long word = strtol(buf, &endp, 16); 81 | if (!endp || endp == buf || (*endp && *endp != ':') 82 | || errno == ERANGE || word < 0 || word > 0xffff) return -1; 83 | if (len >= 16) return -1; 84 | buf = endp; 85 | if (taillen < 0) 86 | { 87 | data[len++] = word >> 8; 88 | data[len++] = word & 0xff; 89 | } 90 | else 91 | { 92 | taildat[taillen++] = word >> 8; 93 | taildat[taillen++] = word & 0xff; 94 | len += 2; 95 | } 96 | 97 | if (*buf && *buf == ':' && buf[1] != ':') ++buf; 98 | } 99 | 100 | if (taillen > 0) memcpy(data + 16 - taillen, taildat, taillen); 101 | return 0; 102 | } 103 | 104 | static void toString(PSC_IpAddr *self) 105 | { 106 | int len = 0; 107 | if (self->proto == PSC_P_IPv4) 108 | { 109 | len = sprintf(self->str, "%hhu.%hhu.%hhu.%hhu", 110 | self->data[12], self->data[13], 111 | self->data[14], self->data[15]); 112 | if (len < 0) len = 0; 113 | if (self->prefixlen < 32) sprintf(self->str + len, 114 | "/%u", self->prefixlen); 115 | } 116 | else 117 | { 118 | unsigned word[8]; 119 | int gap = 0; 120 | int gaplen = 0; 121 | for (int i = 0; i < 8; ++i) 122 | { 123 | if (!(word[i] = (self->data[2*i] << 8) | self->data[2*i+1])) 124 | { 125 | if (i > gap + gaplen) 126 | { 127 | gap = i; 128 | gaplen = 1; 129 | } 130 | else ++gaplen; 131 | } 132 | } 133 | int needcolon = 0; 134 | for (int i = 0; i < 8;) 135 | { 136 | if (i == gap && gaplen > 0) 137 | { 138 | self->str[len++] = ':'; 139 | self->str[len++] = ':'; 140 | self->str[len] = 0; 141 | needcolon = 0; 142 | i += gaplen; 143 | continue; 144 | } 145 | if (needcolon) self->str[len++] = ':'; 146 | needcolon = 1; 147 | int rc = sprintf(self->str + len, "%x", word[i]); 148 | if (rc < 0) self->str[len] = 0; 149 | else len += rc; 150 | ++i; 151 | } 152 | if (self->prefixlen < 128) sprintf(self->str+len, 153 | "/%u", self->prefixlen); 154 | } 155 | } 156 | 157 | SOLOCAL PSC_IpAddr *PSC_IpAddr_fromSockAddr(const struct sockaddr *addr) 158 | { 159 | uint8_t data[16] = {0}; 160 | PSC_Proto proto = PSC_P_ANY; 161 | unsigned prefixlen = 0; 162 | int port = -1; 163 | const struct sockaddr_in *sain; 164 | const struct sockaddr_in6 *sain6; 165 | 166 | switch (addr->sa_family) 167 | { 168 | case AF_INET: 169 | sain = (const struct sockaddr_in *)addr; 170 | memcpy(data+12, &sain->sin_addr.s_addr, 4); 171 | proto = PSC_P_IPv4; 172 | prefixlen = 32; 173 | port = sain->sin_port; 174 | break; 175 | 176 | case AF_INET6: 177 | sain6 = (const struct sockaddr_in6 *)addr; 178 | memcpy(data, sain6->sin6_addr.s6_addr, 16); 179 | proto = PSC_P_IPv6; 180 | prefixlen = 128; 181 | port = sain6->sin6_port; 182 | break; 183 | 184 | default: 185 | return 0; 186 | } 187 | 188 | PSC_IpAddr *self = PSC_malloc(sizeof *self); 189 | #ifdef IPA_NO_ATOMICS 190 | if (pthread_mutex_init(&self->reflock, 0) != 0) 191 | { 192 | free(self); 193 | return 0; 194 | } 195 | self->refcnt = 1; 196 | #else 197 | atomic_store_explicit(&self->refcnt, 1, memory_order_release); 198 | #endif 199 | self->proto = proto; 200 | self->prefixlen = prefixlen; 201 | self->port = port; 202 | memcpy(self->data, data, 16); 203 | toString(self); 204 | 205 | return self; 206 | } 207 | 208 | SOEXPORT PSC_IpAddr *PSC_IpAddr_create(const char *str) 209 | { 210 | uint8_t data[16] = { 0 }; 211 | char buf[44]; 212 | size_t inlen = strlen(str); 213 | if (inlen < 2 || inlen > 43) return 0; 214 | strcpy(buf, str); 215 | 216 | unsigned prefixlen = (unsigned)-1; 217 | char *prstr = strchr(buf, '/'); 218 | if (prstr) 219 | { 220 | *prstr++ = 0; 221 | errno = 0; 222 | char *endp = 0; 223 | long prefixval = strtol(prstr, &endp, 10); 224 | if (!endp || endp == prstr || *endp || errno == ERANGE 225 | || prefixval < 0 || prefixval > 128) return 0; 226 | prefixlen = prefixval; 227 | } 228 | 229 | PSC_Proto proto = PSC_P_ANY; 230 | if (parsev4(data, buf) == 0) 231 | { 232 | if (prefixlen == (unsigned)-1) prefixlen = 32; 233 | else if (prefixlen > 32) return 0; 234 | proto = PSC_P_IPv4; 235 | } 236 | else if (parsev6(data, buf) == 0) 237 | { 238 | if (prefixlen == (unsigned)-1) prefixlen = 128; 239 | proto = PSC_P_IPv6; 240 | } 241 | else return 0; 242 | 243 | PSC_IpAddr *self = PSC_malloc(sizeof *self); 244 | #ifdef IPA_NO_ATOMICS 245 | if (pthread_mutex_init(&self->reflock, 0) != 0) 246 | { 247 | free(self); 248 | return 0; 249 | } 250 | self->refcnt = 1; 251 | #else 252 | atomic_store_explicit(&self->refcnt, 1, memory_order_release); 253 | #endif 254 | self->proto = proto; 255 | self->prefixlen = prefixlen; 256 | self->port = -1; 257 | memcpy(self->data, data, 16); 258 | toString(self); 259 | 260 | return self; 261 | } 262 | 263 | SOEXPORT PSC_IpAddr *PSC_IpAddr_ref(const PSC_IpAddr *self) 264 | { 265 | PSC_IpAddr *mut = (PSC_IpAddr *)self; 266 | #ifdef IPA_NO_ATOMICS 267 | pthread_mutex_lock(&mut->reflock); 268 | ++mut->refcnt; 269 | pthread_mutex_unlock(&mut->reflock); 270 | #else 271 | atomic_fetch_add_explicit(&mut->refcnt, 1, memory_order_acq_rel); 272 | #endif 273 | return mut; 274 | } 275 | 276 | SOEXPORT PSC_IpAddr *PSC_IpAddr_tov4(const PSC_IpAddr *self, 277 | const PSC_IpAddr **prefixes) 278 | { 279 | if (self->prefixlen < 96) return 0; 280 | 281 | int matches = 0; 282 | for (const PSC_IpAddr **prefix = prefixes; *prefix; ++prefix) 283 | { 284 | if (PSC_IpAddr_prefixlen(*prefix) == 96 && 285 | PSC_IpAddr_matches(self, *prefix)) 286 | { 287 | matches = 1; 288 | break; 289 | } 290 | } 291 | if (!matches) return 0; 292 | 293 | PSC_IpAddr *mapped = PSC_malloc(sizeof *mapped); 294 | #ifdef IPA_NO_ATOMICS 295 | if (pthread_mutex_init(&mapped->reflock, 0) != 0) 296 | { 297 | free(mapped); 298 | return 0; 299 | } 300 | mapped->refcnt = 1; 301 | #else 302 | atomic_store_explicit(&mapped->refcnt, 1, memory_order_release); 303 | #endif 304 | mapped->proto = PSC_P_IPv4; 305 | mapped->prefixlen = self->prefixlen - 96; 306 | mapped->port = -1; 307 | memset(mapped->data, 0, 12); 308 | memcpy(mapped->data+12, self->data+12, 4); 309 | toString(mapped); 310 | 311 | return mapped; 312 | } 313 | 314 | SOEXPORT PSC_IpAddr *PSC_IpAddr_tov6(const PSC_IpAddr *self, 315 | const PSC_IpAddr *prefix) 316 | { 317 | if (self->proto != PSC_P_IPv4 || prefix->prefixlen != 96) return 0; 318 | 319 | PSC_IpAddr *mapped = PSC_malloc(sizeof *mapped); 320 | #ifdef IPA_NO_ATOMICS 321 | if (pthread_mutex_init(&mapped->reflock, 0) != 0) 322 | { 323 | free(mapped); 324 | return 0; 325 | } 326 | mapped->refcnt = 1; 327 | #else 328 | atomic_store_explicit(&mapped->refcnt, 1, memory_order_release); 329 | #endif 330 | mapped->proto = PSC_P_IPv6; 331 | mapped->prefixlen = self->prefixlen + 96; 332 | memcpy(mapped->data, prefix->data, 12); 333 | memcpy(mapped->data+12, self->data+12, 4); 334 | toString(mapped); 335 | 336 | return mapped; 337 | } 338 | 339 | SOEXPORT PSC_Proto PSC_IpAddr_proto(const PSC_IpAddr *self) 340 | { 341 | return self->proto; 342 | } 343 | 344 | SOEXPORT unsigned PSC_IpAddr_prefixlen(const PSC_IpAddr *self) 345 | { 346 | return self->prefixlen; 347 | } 348 | 349 | SOLOCAL int PSC_IpAddr_port(const PSC_IpAddr *self) 350 | { 351 | return self->port; 352 | } 353 | 354 | SOLOCAL int PSC_IpAddr_sockAddr(const PSC_IpAddr *self, 355 | struct sockaddr *addr) 356 | { 357 | if (self->proto == PSC_P_IPv4) 358 | { 359 | struct sockaddr_in *sain = (struct sockaddr_in *)addr; 360 | memset(sain, 0, sizeof *sain); 361 | sain->sin_family = AF_INET; 362 | memcpy(&sain->sin_addr.s_addr, self->data+12, 4); 363 | return 0; 364 | } 365 | if (self->proto == PSC_P_IPv6) 366 | { 367 | struct sockaddr_in6 *sain6 = (struct sockaddr_in6 *)addr; 368 | memset(sain6, 0, sizeof *sain6); 369 | sain6->sin6_family = AF_INET6; 370 | memcpy(sain6->sin6_addr.s6_addr, self->data, 16); 371 | return 0; 372 | } 373 | return -1; 374 | } 375 | 376 | SOEXPORT const char *PSC_IpAddr_string(const PSC_IpAddr *self) 377 | { 378 | return self->str; 379 | } 380 | 381 | SOEXPORT int PSC_IpAddr_equals(const PSC_IpAddr *self, const PSC_IpAddr *other) 382 | { 383 | if (self->proto != other->proto) return 0; 384 | if (self->prefixlen != other->prefixlen) return 0; 385 | return !memcmp(self->data, other->data, 16); 386 | } 387 | 388 | SOEXPORT int PSC_IpAddr_matches(const PSC_IpAddr *self, 389 | const PSC_IpAddr *prefix) 390 | { 391 | if (self->proto != prefix->proto) return 0; 392 | if (self->prefixlen < prefix->prefixlen) return 0; 393 | 394 | unsigned bytes = prefix->prefixlen / 8; 395 | if (memcmp(self->data, prefix->data, bytes)) return 0; 396 | unsigned bits = prefix->prefixlen % 8; 397 | if (!bits) return 1; 398 | uint8_t mask = (0xff << (8 - bits)); 399 | return ((self->data[bytes] & mask) == (prefix->data[bytes] & mask)); 400 | } 401 | 402 | SOEXPORT void PSC_IpAddr_destroy(PSC_IpAddr *self) 403 | { 404 | if (!self) return; 405 | #ifdef IPA_NO_ATOMICS 406 | pthread_mutex_lock(&self->reflock); 407 | unsigned refcnt = --self->refcnt; 408 | pthread_mutex_unlock(&self->reflock); 409 | if (refcnt) return; 410 | pthread_mutex_destroy(&self->reflock); 411 | #else 412 | if (atomic_fetch_sub_explicit(&self->refcnt, 1, memory_order_acq_rel) > 1) 413 | { 414 | return; 415 | } 416 | #endif 417 | free(self); 418 | } 419 | 420 | -------------------------------------------------------------------------------- /src/lib/core/ipaddr.h: -------------------------------------------------------------------------------- 1 | #ifndef POSER_CORE_INT_IPADDR_H 2 | #define POSER_CORE_INT_IPADDR_H 3 | 4 | #include 5 | 6 | #include 7 | 8 | PSC_IpAddr *PSC_IpAddr_fromSockAddr(const struct sockaddr *addr); 9 | int PSC_IpAddr_port(const PSC_IpAddr *self); 10 | int PSC_IpAddr_sockAddr(const PSC_IpAddr *self, struct sockaddr *addr); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /src/lib/core/list.c: -------------------------------------------------------------------------------- 1 | #define _DEFAULT_SOURCE 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #define INITIALCAPA 8 10 | 11 | typedef struct PSC_ListItem 12 | { 13 | void *obj; 14 | void (*deleter)(void *); 15 | } PSC_ListItem; 16 | 17 | struct PSC_List 18 | { 19 | PSC_ListItem *items; 20 | size_t capa; 21 | size_t count; 22 | }; 23 | 24 | struct PSC_ListIterator 25 | { 26 | size_t count; 27 | size_t pos; 28 | PSC_ListItem items[]; 29 | }; 30 | 31 | SOEXPORT PSC_List *PSC_List_create(void) 32 | { 33 | PSC_List *self = PSC_malloc(sizeof *self); 34 | self->capa = INITIALCAPA; 35 | self->count = 0; 36 | self->items = PSC_malloc(self->capa * sizeof *self->items); 37 | return self; 38 | } 39 | 40 | SOEXPORT size_t PSC_List_size(const PSC_List *self) 41 | { 42 | return self->count; 43 | } 44 | 45 | SOEXPORT void *PSC_List_at(const PSC_List *self, size_t idx) 46 | { 47 | if (idx >= self->count) return 0; 48 | return self->items[idx].obj; 49 | } 50 | 51 | SOEXPORT void PSC_List_append(PSC_List *self, void *obj, void (*deleter)(void *)) 52 | { 53 | if (self->count == self->capa) 54 | { 55 | self->capa *= 2; 56 | self->items = PSC_realloc(self->items, 57 | self->capa * sizeof *self->items); 58 | } 59 | self->items[self->count].obj = obj; 60 | self->items[self->count].deleter = deleter; 61 | ++self->count; 62 | } 63 | 64 | static void removeAt(PSC_List *self, size_t i, int delete) 65 | { 66 | if (delete && self->items[i].deleter) 67 | { 68 | self->items[i].deleter(self->items[i].obj); 69 | } 70 | if (i < self->count - 1) 71 | { 72 | memmove(self->items+i, self->items+i+1, 73 | (self->count-i-1) * sizeof *self->items); 74 | } 75 | --self->count; 76 | } 77 | 78 | SOEXPORT void PSC_List_remove(PSC_List *self, void *obj) 79 | { 80 | for (size_t i = 0; i < self->count; ++i) 81 | { 82 | if (self->items[i].obj == obj) 83 | { 84 | removeAt(self, i, 0); 85 | break; 86 | } 87 | } 88 | } 89 | 90 | SOEXPORT void PSC_List_removeAll(PSC_List *self, 91 | int (*matcher)(void *, const void *), const void *arg) 92 | { 93 | for (size_t i = 0; i < self->count; ++i) 94 | { 95 | if (matcher(self->items[i].obj, arg)) 96 | { 97 | removeAt(self, i, 1); 98 | --i; 99 | } 100 | } 101 | } 102 | 103 | SOEXPORT PSC_ListIterator *PSC_List_iterator(const PSC_List *self) 104 | { 105 | PSC_ListIterator *iter = PSC_malloc(sizeof *iter + 106 | self->count * sizeof *self->items); 107 | iter->count = self->count; 108 | iter->pos = self->count; 109 | memcpy(iter->items, self->items, self->count * sizeof *self->items); 110 | return iter; 111 | } 112 | 113 | SOEXPORT void PSC_List_clear(PSC_List *self) 114 | { 115 | for (size_t i = 0; i < self->count; ++i) 116 | { 117 | if (self->items[i].deleter) self->items[i].deleter(self->items[i].obj); 118 | } 119 | self->count = 0; 120 | } 121 | 122 | SOEXPORT void PSC_List_destroy(PSC_List *self) 123 | { 124 | if (!self) return; 125 | PSC_List_clear(self); 126 | free(self->items); 127 | free(self); 128 | } 129 | 130 | SOEXPORT int PSC_ListIterator_moveNext(PSC_ListIterator *self) 131 | { 132 | if (self->pos >= self->count) self->pos = 0; 133 | else ++self->pos; 134 | return self->pos < self->count; 135 | } 136 | 137 | SOEXPORT void *PSC_ListIterator_current(const PSC_ListIterator *self) 138 | { 139 | if (self->pos >= self->count) return 0; 140 | return self->items[self->pos].obj; 141 | } 142 | 143 | SOEXPORT void PSC_ListIterator_destroy(PSC_ListIterator *self) 144 | { 145 | free(self); 146 | } 147 | 148 | SOEXPORT PSC_List *PSC_List_fromString(const char *str, const char *delim) 149 | { 150 | PSC_List *list = 0; 151 | char *buf = PSC_copystr(str); 152 | char *bufp = buf; 153 | char *word = 0; 154 | 155 | while ((word = strsep(&bufp, delim))) 156 | { 157 | if (*word) 158 | { 159 | if (!list) list = PSC_List_create(); 160 | PSC_List_append(list, PSC_copystr(word), free); 161 | } 162 | } 163 | 164 | free(buf); 165 | return list; 166 | } 167 | -------------------------------------------------------------------------------- /src/lib/core/log.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 200112L 2 | 3 | #include "log.h" 4 | #include "service.h" 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | static PSC_LogWriter currentwriter = 0; 17 | static void *writerdata; 18 | static PSC_LogLevel maxlevel = PSC_L_INFO; 19 | static int logsilent = 0; 20 | static int logasync = 0; 21 | 22 | static const char *levels[] = 23 | { 24 | "[FATAL]", 25 | "[ERROR]", 26 | "[WARN ]", 27 | "[INFO ]", 28 | "[DEBUG]" 29 | }; 30 | 31 | static int syslogLevels[] = 32 | { 33 | LOG_CRIT, 34 | LOG_ERR, 35 | LOG_WARNING, 36 | LOG_INFO, 37 | LOG_DEBUG 38 | }; 39 | 40 | typedef struct LogJobArgs 41 | { 42 | PSC_LogLevel level; 43 | PSC_LogWriter writer; 44 | void *writerdata; 45 | char message[]; 46 | } LogJobArgs; 47 | 48 | static void logmsgJobProc(void *arg); 49 | static void writeFile(PSC_LogLevel level, const char *message, void *data) 50 | ATTR_NONNULL((2)); 51 | static void writeSyslog(PSC_LogLevel level, const char *message, void *data) 52 | ATTR_NONNULL((2)); 53 | 54 | static void logmsgJobProc(void *arg) 55 | { 56 | LogJobArgs *lja = arg; 57 | lja->writer(lja->level, lja->message, lja->writerdata); 58 | free(lja); 59 | } 60 | 61 | static void writeFile(PSC_LogLevel level, const char *message, void *data) 62 | { 63 | FILE *target = data; 64 | fprintf(target, "%s %s\n", levels[level], message); 65 | fflush(target); 66 | } 67 | 68 | static void writeSyslog(PSC_LogLevel level, const char *message, void *data) 69 | { 70 | syslog(syslogLevels[level], "%s", message); 71 | if (data) writeFile(level, message, stderr); 72 | } 73 | 74 | SOEXPORT void PSC_Log_setFileLogger(FILE *file) 75 | { 76 | currentwriter = writeFile; 77 | writerdata = file; 78 | } 79 | 80 | SOEXPORT void PSC_Log_setSyslogLogger(const char *ident, 81 | int facility, int withStderr) 82 | { 83 | int logopts = LOG_PID; 84 | #ifdef LOG_PERROR 85 | if (withStderr) logopts |= LOG_PERROR; 86 | writerdata = 0; 87 | #else 88 | writerdata = withStderr ? &writerdata : 0; 89 | #endif 90 | openlog(ident, logopts, facility); 91 | currentwriter = writeSyslog; 92 | } 93 | 94 | SOEXPORT void PSC_Log_setCustomLogger(PSC_LogWriter writer, void *data) 95 | { 96 | currentwriter = writer; 97 | writerdata = data; 98 | } 99 | 100 | SOEXPORT void PSC_Log_setMaxLogLevel(PSC_LogLevel level) 101 | { 102 | maxlevel = level; 103 | } 104 | 105 | SOEXPORT void PSC_Log_setSilent(int silent) 106 | { 107 | logsilent = silent; 108 | } 109 | 110 | SOEXPORT void PSC_Log_setAsync(int async) 111 | { 112 | logasync = async; 113 | } 114 | 115 | SOEXPORT int PSC_Log_enabled(PSC_LogLevel level) 116 | { 117 | if (logsilent && level > PSC_L_ERROR) return 0; 118 | return level <= maxlevel; 119 | } 120 | 121 | static void enqueueLogJob(void *arg) 122 | { 123 | PSC_ThreadJob *job = PSC_ThreadJob_create(logmsgJobProc, arg, 4000); 124 | PSC_ThreadPool_enqueue(job); 125 | } 126 | 127 | SOEXPORT void PSC_Log_msg(PSC_LogLevel level, const char *message) 128 | { 129 | if (!currentwriter) return; 130 | if (logsilent && level > PSC_L_ERROR) return; 131 | if (level > maxlevel) return; 132 | if (logasync && PSC_Service_running() && PSC_ThreadPool_active()) 133 | { 134 | size_t msgsize = strlen(message)+1; 135 | LogJobArgs *lja = PSC_malloc(sizeof *lja + msgsize); 136 | lja->level = level; 137 | lja->writer = currentwriter; 138 | lja->writerdata = writerdata; 139 | strcpy(lja->message, message); 140 | int thrno = PSC_Service_threadNo(); 141 | if (thrno < -1) thrno = -1; 142 | PSC_Service_runOnThread(thrno, enqueueLogJob, lja); 143 | } 144 | else currentwriter(level, message, writerdata); 145 | } 146 | 147 | SOEXPORT void PSC_Log_fmt(PSC_LogLevel level, const char *format, ...) 148 | { 149 | if (!currentwriter) return; 150 | if (logsilent && level > PSC_L_ERROR) return; 151 | if (level > maxlevel) return; 152 | char buf[PSC_MAXLOGLINE]; 153 | va_list ap; 154 | va_start(ap, format); 155 | vsnprintf(buf, PSC_MAXLOGLINE, format, ap); 156 | va_end(ap); 157 | PSC_Log_msg(level, buf); 158 | } 159 | 160 | SOEXPORT void PSC_Log_err(PSC_LogLevel level, const char *message) 161 | { 162 | if (!currentwriter) return; 163 | if (logsilent && level > PSC_L_ERROR) return; 164 | if (level > maxlevel) return; 165 | char errstr[128]; 166 | strerror_r(errno, errstr, sizeof errstr); 167 | PSC_Log_fmt(level, "%s: %s", message, errstr); 168 | } 169 | 170 | SOEXPORT void PSC_Log_errfmt(PSC_LogLevel level, const char *format, ...) 171 | { 172 | if (!currentwriter) return; 173 | if (logsilent && level > PSC_L_ERROR) return; 174 | if (level > maxlevel) return; 175 | char fmt[PSC_MAXLOGLINE]; 176 | char errstr[128]; 177 | if (strlen(format) < PSC_MAXLOGLINE - 4) 178 | { 179 | strerror_r(errno, errstr, sizeof errstr); 180 | snprintf(fmt, sizeof fmt, "%s: %s", format, errstr); 181 | format = fmt; 182 | } 183 | char buf[PSC_MAXLOGLINE]; 184 | va_list ap; 185 | va_start(ap, format); 186 | vsnprintf(buf, PSC_MAXLOGLINE, format, ap); 187 | va_end(ap); 188 | PSC_Log_msg(level, buf); 189 | } 190 | 191 | 192 | SOLOCAL void PSC_Log_setPanic(void) 193 | { 194 | logasync = 0; 195 | if (!currentwriter) 196 | { 197 | currentwriter = writeFile; 198 | writerdata = stderr; 199 | } 200 | } 201 | 202 | -------------------------------------------------------------------------------- /src/lib/core/log.h: -------------------------------------------------------------------------------- 1 | #ifndef POSER_CORE_INT_LOG_H 2 | #define POSER_CORE_INT_LOG_H 3 | 4 | #include 5 | 6 | void PSC_Log_setPanic(void); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /src/lib/core/process.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 200112L 2 | 3 | #include 4 | 5 | #include "connection.h" 6 | #include "event.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | typedef enum ChildStatus 21 | { 22 | CS_NONE, 23 | CS_RUNNING, 24 | CS_EXITED, 25 | CS_SIGNALED 26 | } ChildStatus; 27 | 28 | struct PSC_EAProcessDone 29 | { 30 | int exitval; 31 | ChildStatus status; 32 | }; 33 | 34 | struct PSC_ProcessOpts 35 | { 36 | const char **args; 37 | int argc; 38 | int argscapa; 39 | int execError; 40 | PSC_StreamAction actions[3]; 41 | }; 42 | 43 | struct PSC_Process 44 | { 45 | PSC_Event done; 46 | PSC_Timer *killtimer; 47 | PSC_Connection *streams[3]; 48 | int execError; 49 | int argc; 50 | int thrno; 51 | pid_t pid; 52 | PSC_EAProcessDone doneArgs; 53 | PSC_StreamAction actions[3]; 54 | char *args[]; 55 | }; 56 | 57 | SOEXPORT PSC_ProcessOpts *PSC_ProcessOpts_create(void) 58 | { 59 | PSC_ProcessOpts *self = PSC_malloc(sizeof *self); 60 | memset(self, 0, sizeof *self); 61 | self->argc = 0; 62 | self->execError = PSC_ERR_EXEC; 63 | return self; 64 | } 65 | 66 | static void addArg(PSC_ProcessOpts *self, const char *arg) 67 | { 68 | if (self->argc == self->argscapa) 69 | { 70 | self->argscapa += 16; 71 | self->args = PSC_realloc(self->args, self->argscapa * 72 | sizeof *self->args); 73 | } 74 | self->args[self->argc++] = arg; 75 | } 76 | 77 | SOEXPORT void PSC_ProcessOpts_setName(PSC_ProcessOpts *self, const char *name) 78 | { 79 | if (self->argc == 0) addArg(self, name); 80 | else self->args[0] = name; 81 | } 82 | 83 | SOEXPORT void PSC_ProcessOpts_addArg(PSC_ProcessOpts *self, const char *arg) 84 | { 85 | if (self->argc == 0) addArg(self, 0); 86 | addArg(self, arg); 87 | } 88 | 89 | SOEXPORT int PSC_ProcessOpts_streamAction(PSC_ProcessOpts *self, 90 | PSC_StreamType stream, PSC_StreamAction action) 91 | { 92 | if (stream < PSC_ST_STDIN || stream > PSC_ST_STDERR) return -1; 93 | if (action < PSC_SA_LEAVE || action > PSC_SA_PIPE) return -1; 94 | self->actions[stream] = action; 95 | return 0; 96 | } 97 | 98 | SOEXPORT int PSC_ProcessOpts_setExecError(PSC_ProcessOpts *self, 99 | int execError) 100 | { 101 | if (execError < -128 || execError > 127) return -1; 102 | self->execError = execError; 103 | return 0; 104 | } 105 | 106 | SOEXPORT void PSC_ProcessOpts_destroy(PSC_ProcessOpts *self) 107 | { 108 | if (!self) return; 109 | free(self->args); 110 | free(self); 111 | } 112 | 113 | SOEXPORT PSC_Process *PSC_Process_create(const PSC_ProcessOpts *opts) 114 | { 115 | int argc = opts->argc; 116 | if (argc == 0) argc = 1; 117 | PSC_Process *self = PSC_malloc(sizeof *self + 118 | (argc + 1) * sizeof *self->args); 119 | memset(self, 0, sizeof *self); 120 | PSC_Event_initStatic(&self->done, self); 121 | self->execError = opts->execError; 122 | self->argc = argc; 123 | self->thrno = PSC_Service_threadNo(); 124 | memcpy(self->actions, opts->actions, sizeof self->actions); 125 | if (opts->argc == 0) self->args[0] = 0; 126 | else for (int i = 0; i < argc; ++i) 127 | { 128 | self->args[i] = opts->args[i] ? PSC_copystr(opts->args[i]) : 0; 129 | } 130 | self->args[argc] = 0; 131 | return self; 132 | } 133 | 134 | static int createPipe(int *fds, int check) 135 | { 136 | if (pipe(fds) < 0) 137 | { 138 | PSC_Log_err(PSC_L_ERROR, "process: Error creating pipe"); 139 | return -1; 140 | } 141 | if (!PSC_Service_isValidFd(fds[check], "process")) return -1; 142 | fcntl(fds[check], F_SETFL, fcntl(fds[check], F_GETFL) | O_NONBLOCK); 143 | return 0; 144 | } 145 | 146 | static const int stdfds[] = { STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO }; 147 | static const int stdfddir[] = { 1, 0, 0 }; 148 | 149 | static void prepareChild(const PSC_StreamAction *actions, int (*pipefd)[2], 150 | int execError) 151 | { 152 | int nullfd = -1; 153 | 154 | for (int i = PSC_ST_STDIN; i <= PSC_ST_STDERR; ++i) 155 | { 156 | switch (actions[i]) 157 | { 158 | case PSC_SA_CLOSE: 159 | close(stdfds[i]); 160 | break; 161 | 162 | case PSC_SA_NULL: 163 | if (nullfd < 0 && (nullfd = open("/dev/null", O_RDWR) < 0)) 164 | { 165 | goto fail; 166 | } 167 | if (dup2(nullfd, stdfds[i]) < 0) goto fail; 168 | break; 169 | 170 | case PSC_SA_PIPE: 171 | if (dup2(pipefd[i][!stdfddir[i]], stdfds[i]) < 0) goto fail; 172 | close(pipefd[i][stdfddir[i]]); 173 | break; 174 | 175 | default: 176 | break; 177 | } 178 | } 179 | if (nullfd >= 0) close(nullfd); 180 | 181 | sigset_t allsigs; 182 | sigfillset(&allsigs); 183 | sigprocmask(SIG_UNBLOCK, &allsigs, 0); 184 | 185 | return; 186 | 187 | fail: 188 | exit(execError); 189 | } 190 | 191 | static void tryDestroy(void *arg) 192 | { 193 | PSC_Process *self = arg; 194 | if (self->doneArgs.status == CS_RUNNING) return; 195 | if (self->streams[PSC_ST_STDIN]) return; 196 | if (self->streams[PSC_ST_STDOUT]) return; 197 | if (self->streams[PSC_ST_STDERR]) return; 198 | 199 | PSC_Event_raise(&self->done, 0, &self->doneArgs); 200 | 201 | for (int i = 0; i < self->argc; ++i) free(self->args[i]); 202 | PSC_Event_destroyStatic(&self->done); 203 | free(self); 204 | } 205 | 206 | static void streamClosed(void *receiver, void *sender, void *args) 207 | { 208 | (void)args; 209 | 210 | PSC_Process *self = receiver; 211 | PSC_Connection *conn = sender; 212 | 213 | for (int i = PSC_ST_STDIN; i <= PSC_ST_STDERR; ++i) 214 | { 215 | if (self->streams[i] == conn) self->streams[i] = 0; 216 | } 217 | 218 | tryDestroy(self); 219 | } 220 | 221 | struct endprocargs 222 | { 223 | PSC_Process *proc; 224 | pid_t pid; 225 | int signo; 226 | int status; 227 | }; 228 | 229 | static void endprocess(void *arg) 230 | { 231 | struct endprocargs *epa = arg; 232 | 233 | if (epa->pid != epa->proc->pid) goto done; 234 | 235 | PSC_Timer_destroy(epa->proc->killtimer); 236 | epa->proc->killtimer = 0; 237 | 238 | if (epa->proc->streams[PSC_ST_STDIN]) 239 | { 240 | PSC_Connection_close(epa->proc->streams[PSC_ST_STDIN], 0); 241 | } 242 | 243 | if (epa->signo) 244 | { 245 | epa->proc->doneArgs.status = CS_SIGNALED; 246 | epa->proc->doneArgs.exitval = epa->signo; 247 | } 248 | else 249 | { 250 | epa->proc->doneArgs.status = CS_EXITED; 251 | epa->proc->doneArgs.exitval = epa->status; 252 | } 253 | 254 | tryDestroy(epa->proc); 255 | done: 256 | free(epa); 257 | } 258 | 259 | static void childExited(void *receiver, void *sender, void *args) 260 | { 261 | (void)sender; 262 | 263 | PSC_EAChildExited *ea = args; 264 | struct endprocargs *epa = PSC_malloc(sizeof *epa); 265 | epa->proc = receiver; 266 | epa->pid = PSC_EAChildExited_pid(ea); 267 | epa->signo = PSC_EAChildExited_signal(ea); 268 | epa->status = PSC_EAChildExited_status(ea); 269 | 270 | PSC_Service_runOnThread(epa->proc->thrno, endprocess, epa); 271 | } 272 | 273 | struct createpipeconnargs 274 | { 275 | PSC_StreamCallback cb; 276 | PSC_Process *proc; 277 | void *obj; 278 | int fd; 279 | ConnectionCreateMode mode; 280 | PSC_StreamType type; 281 | }; 282 | 283 | static void createpipeconn(void *arg) 284 | { 285 | struct createpipeconnargs *cpca = arg; 286 | PSC_Process *self = cpca->proc; 287 | ConnOpts opts; 288 | memset(&opts, 0, sizeof opts); 289 | opts.rdbufsz = DEFRDBUFSZ; 290 | opts.createmode = cpca->mode; 291 | 292 | self->streams[cpca->type] = PSC_Connection_create(cpca->fd, &opts); 293 | PSC_Event_register(PSC_Connection_closed(self->streams[cpca->type]), 294 | self, streamClosed, 0); 295 | cpca->cb(cpca->obj, cpca->type, self->streams[cpca->type]); 296 | free(cpca); 297 | } 298 | 299 | struct runprocessargs 300 | { 301 | PSC_StreamCallback cb; 302 | PSC_ProcessMain main; 303 | PSC_Process *proc; 304 | void *obj; 305 | const char *path; 306 | }; 307 | 308 | static void runprocess(void *arg) 309 | { 310 | struct runprocessargs *rpa = arg; 311 | PSC_Process *self = rpa->proc; 312 | 313 | if (self->pid) return; 314 | 315 | int rc = -1; 316 | int pipefd[][2] = {{-1, -1}, {-1, -1}, {-1, -1}}; 317 | 318 | for (int i = PSC_ST_STDIN; i <= PSC_ST_STDERR; ++i) 319 | { 320 | if (self->actions[i] == PSC_SA_PIPE && (!rpa->cb 321 | || createPipe(pipefd[i], stdfddir[i]) < 0)) goto done; 322 | } 323 | 324 | pid_t pid = fork(); 325 | if (pid < 0) 326 | { 327 | PSC_Log_err(PSC_L_ERROR, "process: cannot fork()"); 328 | goto done; 329 | } 330 | 331 | if (pid == 0) 332 | { 333 | prepareChild(self->actions, pipefd, self->execError); 334 | if (rpa->path) 335 | { 336 | if (self->args[0] == 0) self->args[0] = (char *)rpa->path; 337 | execv(rpa->path, self->args); 338 | exit(self->execError); 339 | } 340 | else 341 | { 342 | exit(rpa->main(self->argc, self->args)); 343 | } 344 | } 345 | 346 | self->pid = pid; 347 | self->doneArgs.status = CS_RUNNING; 348 | PSC_Event_register(PSC_Service_childExited(), self, childExited, pid); 349 | 350 | ConnOpts opts; 351 | memset(&opts, 0, sizeof opts); 352 | opts.rdbufsz = DEFRDBUFSZ; 353 | 354 | for (int i = PSC_ST_STDIN; i <= PSC_ST_STDERR; ++i) 355 | { 356 | if (self->actions[i] == PSC_SA_PIPE) 357 | { 358 | struct createpipeconnargs *cpca = PSC_malloc(sizeof *cpca); 359 | cpca->cb = rpa->cb; 360 | cpca->proc = self; 361 | cpca->obj = rpa->obj; 362 | cpca->fd = pipefd[i][stdfddir[i]]; 363 | cpca->mode = stdfddir[i] ? CCM_PIPEWR : CCM_PIPERD; 364 | cpca->type = i; 365 | pipefd[i][stdfddir[i]] = -1; 366 | PSC_Service_runOnThread(self->thrno, createpipeconn, cpca); 367 | } 368 | } 369 | 370 | rc = 0; 371 | 372 | done: 373 | for (int i = PSC_ST_STDIN; i <= PSC_ST_STDERR; ++i) 374 | { 375 | if (pipefd[i][0] >= 0) close(pipefd[i][0]); 376 | if (pipefd[i][1] >= 0) close(pipefd[i][1]); 377 | } 378 | if (rc != 0) 379 | { 380 | self->doneArgs.exitval = self->execError; 381 | self->doneArgs.status = CS_EXITED; 382 | PSC_Service_runOnThread(self->thrno, tryDestroy, self); 383 | } 384 | } 385 | 386 | SOEXPORT void PSC_Process_exec(PSC_Process *self, void *obj, 387 | PSC_StreamCallback cb, const char *path) 388 | { 389 | struct runprocessargs *rpa = PSC_malloc(sizeof *rpa); 390 | rpa->cb = cb; 391 | rpa->main = 0; 392 | rpa->proc = self; 393 | rpa->obj = obj; 394 | rpa->path = path; 395 | PSC_Service_runOnThread(-1, runprocess, rpa); 396 | } 397 | 398 | SOEXPORT void PSC_Process_run(PSC_Process *self, void *obj, 399 | PSC_StreamCallback cb, PSC_ProcessMain main) 400 | { 401 | struct runprocessargs *rpa = PSC_malloc(sizeof *rpa); 402 | rpa->cb = cb; 403 | rpa->main = main; 404 | rpa->proc = self; 405 | rpa->obj = obj; 406 | rpa->path = 0; 407 | PSC_Service_runOnThread(-1, runprocess, rpa); 408 | } 409 | 410 | static void forcekill(void *receiver, void *sender, void *args) 411 | { 412 | (void)sender; 413 | (void)args; 414 | 415 | PSC_Process *self = receiver; 416 | kill(self->pid, SIGKILL); 417 | } 418 | 419 | SOEXPORT int PSC_Process_stop(PSC_Process *self, unsigned forceMs) 420 | { 421 | if (self->doneArgs.status != CS_RUNNING || self->killtimer) return -1; 422 | if (kill(self->pid, SIGTERM) < 0) return -1; 423 | if (forceMs) 424 | { 425 | self->killtimer = PSC_Timer_create(); 426 | if (!self->killtimer) 427 | { 428 | PSC_Log_msg(PSC_L_ERROR, "process: cannot create timer for " 429 | "force-stopping the child process"); 430 | return -1; 431 | } 432 | PSC_Timer_setMs(self->killtimer, forceMs); 433 | PSC_Event_register(PSC_Timer_expired(self->killtimer), self, 434 | forcekill, 0); 435 | PSC_Timer_start(self->killtimer, 0); 436 | } 437 | return 0; 438 | } 439 | 440 | SOEXPORT PSC_Event *PSC_Process_done(PSC_Process *self) 441 | { 442 | return &self->done; 443 | } 444 | 445 | SOEXPORT pid_t PSC_Process_pid(const PSC_Process *self) 446 | { 447 | return self->doneArgs.status == CS_RUNNING ? self->pid : (pid_t)-1; 448 | } 449 | 450 | SOEXPORT int PSC_EAProcessDone_status(const PSC_EAProcessDone *self) 451 | { 452 | if (self->status != CS_EXITED) return PSC_CHILD_SIGNALED; 453 | return self->exitval; 454 | } 455 | 456 | SOEXPORT int PSC_EAProcessDone_signal(const PSC_EAProcessDone *self) 457 | { 458 | if (self->status != CS_SIGNALED) return 0; 459 | return self->exitval; 460 | } 461 | 462 | -------------------------------------------------------------------------------- /src/lib/core/queue.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #define QUEUEINITIALCAPA 8 9 | 10 | typedef struct QueueEntry 11 | { 12 | void *obj; 13 | void (*deleter)(void *); 14 | } QueueEntry; 15 | 16 | struct PSC_Queue 17 | { 18 | QueueEntry *entries; 19 | size_t count; 20 | size_t capa; 21 | size_t front; 22 | size_t back; 23 | }; 24 | 25 | static inline void expand(PSC_Queue *self) 26 | { 27 | size_t newcapa = 2*self->capa; 28 | self->entries = PSC_realloc(self->entries, 29 | newcapa * sizeof *self->entries); 30 | if (self->front) 31 | { 32 | memcpy(self->entries+self->front+self->capa, self->entries+self->front, 33 | (self->capa-self->front) * sizeof *self->entries); 34 | self->front+=self->capa; 35 | } 36 | else self->back = self->capa; 37 | self->capa = newcapa; 38 | } 39 | 40 | SOEXPORT PSC_Queue *PSC_Queue_create(void) 41 | { 42 | PSC_Queue *self = PSC_malloc(sizeof *self); 43 | self->count = 0; 44 | self->capa = QUEUEINITIALCAPA; 45 | self->front = 0; 46 | self->back = 0; 47 | self->entries = PSC_malloc(self->capa * sizeof *self->entries); 48 | return self; 49 | } 50 | 51 | SOEXPORT void PSC_Queue_enqueue(PSC_Queue *self, void *obj, 52 | void (*deleter)(void *)) 53 | { 54 | if (self->count == self->capa) expand(self); 55 | self->entries[self->back].obj = obj; 56 | self->entries[self->back].deleter = deleter; 57 | if (++self->back == self->capa) self->back = 0; 58 | ++self->count; 59 | } 60 | 61 | SOEXPORT void *PSC_Queue_dequeue(PSC_Queue *self) 62 | { 63 | if (!self->count) return 0; 64 | void *obj = self->entries[self->front].obj; 65 | if (++self->front == self->capa) self->front = 0; 66 | --self->count; 67 | return obj; 68 | } 69 | 70 | SOEXPORT void PSC_Queue_destroy(PSC_Queue *self) 71 | { 72 | if (!self) return; 73 | if (self->count) for (size_t i = 0, pos = self->front; i < self->count; ++i) 74 | { 75 | if (self->entries[pos].deleter) 76 | { 77 | self->entries[pos].deleter(self->entries[pos].obj); 78 | } 79 | if (++pos == self->capa) pos = 0; 80 | } 81 | free(self->entries); 82 | free(self); 83 | } 84 | 85 | -------------------------------------------------------------------------------- /src/lib/core/random.c: -------------------------------------------------------------------------------- 1 | #define _DEFAULT_SOURCE 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #ifndef HAVE_ARC4R 18 | 19 | # ifdef HAVE_GETRANDOM 20 | # include 21 | # else 22 | # include 23 | # include 24 | # endif 25 | 26 | static uint64_t prng(void) 27 | { 28 | static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 29 | static int seeded = 0; 30 | static uint64_t s[4] = { 0 }; 31 | 32 | pthread_mutex_lock(&mutex); 33 | 34 | if (!seeded) 35 | { 36 | seeded = 1; 37 | s[0] = (uint64_t)time(0); 38 | for (int i = 0; i < 100; ++i) prng(); 39 | } 40 | 41 | uint64_t num = s[0] + s[3]; 42 | uint64_t tmp = s[1] << 17; 43 | s[2] ^= s[0]; 44 | s[3] ^= s[1]; 45 | s[1] ^= s[2]; 46 | s[0] ^= s[3]; 47 | s[2] ^= tmp; 48 | s[3] = (s[3]<<45) | (s[3]>>19); 49 | 50 | pthread_mutex_unlock(&mutex); 51 | 52 | return num; 53 | } 54 | #endif 55 | 56 | SOEXPORT size_t PSC_Random_bytes(void *buf, size_t count, 57 | PSC_RandomFlags flags) 58 | { 59 | #ifdef HAVE_ARC4R 60 | (void) flags; 61 | arc4random_buf(buf, count); 62 | return count; 63 | #else 64 | PSC_LogLevel level = PSC_L_DEBUG; 65 | if (flags & PSC_RF_ELOGPSEUDO) level = PSC_L_ERROR; 66 | else if (flags & PSC_RF_WLOGPSEUDO) level = PSC_L_WARNING; 67 | 68 | size_t pos = 0; 69 | uint8_t *b = buf; 70 | # ifdef HAVE_GETRANDOM 71 | unsigned grflags = 0; 72 | if (flags & PSC_RF_NONBLOCK) grflags = GRND_NONBLOCK; 73 | unsigned grflags2 = grflags; 74 | if (flags & PSC_RF_SECURE) 75 | { 76 | grflags2 |= GRND_RANDOM; 77 | } 78 | if (flags & (PSC_RF_WLOGPSEUDO|PSC_RF_ELOGPSEUDO)) 79 | { 80 | grflags |= GRND_RANDOM; 81 | } 82 | int dolog = 0; 83 | while (pos < count) 84 | { 85 | if (dolog == 1 && (flags & (PSC_RF_WLOGPSEUDO|PSC_RF_ELOGPSEUDO))) 86 | { 87 | PSC_Log_msg(level, "random: Could not obtain cryptographically " 88 | "secure random data, trying alternative source (urandom) " 89 | "which might not be secure."); 90 | ++dolog; 91 | } 92 | errno = 0; 93 | ssize_t rc = getrandom(b + pos, count - pos, grflags); 94 | if (rc < 0) 95 | { 96 | if (errno == EINTR) continue; 97 | if (errno == EAGAIN) rc = 0; 98 | else break; 99 | } 100 | if (!dolog) 101 | { 102 | grflags = grflags2; 103 | ++dolog; 104 | } 105 | pos += rc; 106 | } 107 | # else 108 | int rflags = O_RDONLY; 109 | if (flags & PSC_RF_NONBLOCK) rflags |= O_NONBLOCK; 110 | int doswitch = 0; 111 | int rndfd = open("/dev/random", rflags); 112 | if (rndfd < 0) 113 | { 114 | if (flags & PSC_RF_SECURE) return pos; 115 | rndfd = open("/dev/urandom", rflags); 116 | if (rndfd < 0) goto useprng; 117 | doswitch = 1; 118 | } 119 | while (pos < count) 120 | { 121 | if (doswitch == 1 && (flags & (PSC_RF_WLOGPSEUDO|PSC_RF_ELOGPSEUDO))) 122 | { 123 | PSC_Log_msg(level, "random: Could not obtain cryptographically " 124 | "secure random data, trying alternative source (urandom) " 125 | "which might not be secure."); 126 | ++doswitch; 127 | } 128 | errno = 0; 129 | ssize_t rc = read(rndfd, b + pos, count - pos); 130 | if (rc < 0) 131 | { 132 | if (errno == EINTR) continue; 133 | if (errno == EAGAIN) rc = 0; 134 | else break; 135 | } 136 | pos += rc; 137 | if (pos < count && !doswitch) 138 | { 139 | close(rndfd); 140 | if (flags & PSC_RF_SECURE) return pos; 141 | rndfd = open("/dev/urandom", rflags); 142 | if (rndfd < 0) goto useprng; 143 | ++doswitch; 144 | } 145 | } 146 | close(rndfd); 147 | useprng: 148 | # endif 149 | if (pos < count) 150 | { 151 | if (flags & PSC_RF_SECURE) return pos; 152 | if (flags & (PSC_RF_WLOGPSEUDO|PSC_RF_ELOGPSEUDO)) 153 | { 154 | PSC_Log_msg(level, "random: Could not obtain random data, falling " 155 | "back to xorshift-based internal PRNG, which is NOT " 156 | "cryptographically secure."); 157 | } 158 | size_t chunks = (count - pos) / sizeof(uint64_t); 159 | size_t bytes = (count - pos) % sizeof(uint64_t); 160 | if (bytes) 161 | { 162 | uint64_t rn = prng(); 163 | memcpy(b + pos, &rn, bytes); 164 | pos += bytes; 165 | } 166 | for (size_t i = 0; i < chunks; ++i) 167 | { 168 | uint64_t rn = prng(); 169 | memcpy(b + pos, &rn, sizeof rn); 170 | pos += sizeof rn; 171 | } 172 | } 173 | return pos; 174 | #endif 175 | } 176 | 177 | SOEXPORT size_t PSC_Random_string(char *str, size_t size, 178 | PSC_RandomFlags flags, PSC_Base64Flags b64flags) 179 | { 180 | size_t count = PSC_Base64_decodedSize(size - 1); 181 | void *buf = PSC_malloc(count); 182 | size_t got = PSC_Random_bytes(buf, count, flags); 183 | if (got < count) size = PSC_Base64_encodedLen(got) + 1; 184 | PSC_Base64_encodeTo(str, buf, got, b64flags); 185 | free(buf); 186 | return size; 187 | } 188 | 189 | SOEXPORT char *PSC_Random_createStr(size_t count, 190 | PSC_RandomFlags flags, PSC_Base64Flags b64flags) 191 | { 192 | void *buf = PSC_malloc(count); 193 | size_t got = PSC_Random_bytes(buf, count, flags); 194 | char *str = 0; 195 | if (got == count) str = PSC_Base64_encode(buf, count, b64flags); 196 | free(buf); 197 | return str; 198 | } 199 | 200 | -------------------------------------------------------------------------------- /src/lib/core/ratelimit.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 200809L 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #undef RLIM_NO_ATOMICS 14 | #if defined(NO_ATOMICS) || defined(__STDC_NO_ATOMICS__) 15 | # define RLIM_NO_ATOMICS 16 | # include 17 | #else 18 | # include 19 | #endif 20 | 21 | #define MAXCOUNTS 512 22 | 23 | typedef struct Entry 24 | { 25 | #ifndef RLIM_NO_ATOMICS 26 | atomic_flag lock; 27 | #endif 28 | uint16_t last; 29 | uint16_t total; 30 | uint16_t countpos; 31 | uint16_t counts[MAXCOUNTS]; 32 | } Entry; 33 | 34 | #ifdef RLIM_NO_ATOMICS 35 | typedef struct EntryList 36 | { 37 | pthread_mutex_t lock; 38 | size_t nlimits; 39 | Entry entries[]; 40 | } EntryList; 41 | #endif 42 | 43 | typedef struct Limit 44 | { 45 | uint16_t seconds; 46 | uint16_t limit; 47 | uint16_t res; 48 | uint16_t ncounts; 49 | } Limit; 50 | 51 | struct PSC_RateLimit 52 | { 53 | size_t nlimits; 54 | PSC_Dictionary *entries; 55 | uint16_t cleancount; 56 | Limit limits[]; 57 | }; 58 | 59 | struct PSC_RateLimitOpts 60 | { 61 | size_t limits_count; 62 | size_t limits_capa; 63 | Limit *limits; 64 | }; 65 | 66 | #ifdef RLIM_NO_ATOMICS 67 | # define entries(e) ((e)->entries) 68 | static void freeentries(void *obj) 69 | { 70 | if (!obj) return; 71 | EntryList *el = obj; 72 | pthread_mutex_destroy(&el->lock); 73 | free(el); 74 | } 75 | #else 76 | # define entries(e) (e) 77 | # define freeentries free 78 | #endif 79 | 80 | PSC_RateLimit *PSC_RateLimit_create(const PSC_RateLimitOpts *opts) 81 | { 82 | PSC_RateLimit *self = PSC_malloc(sizeof *self 83 | + opts->limits_count * sizeof *self->limits); 84 | self->nlimits = opts->limits_count; 85 | self->entries = PSC_Dictionary_create(freeentries, 1); 86 | memcpy(self->limits, opts->limits, self->nlimits * sizeof *self->limits); 87 | self->cleancount = 2000; 88 | return self; 89 | } 90 | 91 | struct expiredarg 92 | { 93 | Limit *limits; 94 | size_t nlimits; 95 | uint16_t now[]; 96 | }; 97 | 98 | static int expired(const void *key, size_t keysz, void *obj, const void *arg) 99 | { 100 | (void)key; 101 | (void)keysz; 102 | 103 | #ifdef RLIM_NO_ATOMICS 104 | EntryList *e = obj; 105 | #else 106 | Entry *e = obj; 107 | #endif 108 | const struct expiredarg *ea = arg; 109 | 110 | int isexpired = 1; 111 | for (size_t i = 0; i < ea->nlimits; ++i) 112 | { 113 | if (ea->now[i] - entries(e)[i].last < ea->limits[i].ncounts) 114 | { 115 | isexpired = 0; 116 | break; 117 | } 118 | } 119 | return isexpired; 120 | } 121 | 122 | static int checkLimit(Limit *self, Entry *e, struct timespec *ts) 123 | { 124 | uint16_t now = ts->tv_sec / self->res; 125 | #ifndef RLIM_NO_ATOMICS 126 | while (atomic_flag_test_and_set_explicit(&e->lock, memory_order_acq_rel)) ; 127 | #endif 128 | if (e->total) 129 | { 130 | if (now - e->last >= self->ncounts) 131 | { 132 | memset(e, 0, sizeof *e); 133 | e->last = now; 134 | } 135 | else for (; e->last != now; ++e->last) 136 | { 137 | if (++e->countpos == self->ncounts) e->countpos = 0; 138 | e->total -= e->counts[e->countpos]; 139 | e->counts[e->countpos] = 0; 140 | } 141 | } 142 | else e->last = now; 143 | int ok = 0; 144 | if (e->total < self->limit) 145 | { 146 | ++e->counts[e->countpos]; 147 | ++e->total; 148 | ok = 1; 149 | } 150 | #ifndef RLIM_NO_ATOMICS 151 | atomic_flag_clear_explicit(&e->lock, memory_order_release); 152 | #endif 153 | return ok; 154 | } 155 | 156 | int PSC_RateLimit_check(PSC_RateLimit *self, const void *key, size_t keysz) 157 | { 158 | int ok = 1; 159 | struct timespec ts; 160 | if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) return 0; 161 | if (!--self->cleancount) 162 | { 163 | struct expiredarg *ea = PSC_malloc(sizeof *ea 164 | + self->nlimits * sizeof *ea->now); 165 | ea->limits = self->limits; 166 | ea->nlimits = self->nlimits; 167 | for (size_t i = 0; i < self->nlimits; ++i) 168 | { 169 | ea->now[i] = ts.tv_sec / self->limits[i].res; 170 | } 171 | PSC_Dictionary_removeAll(self->entries, expired, ea); 172 | free(ea); 173 | self->cleancount = 2000; 174 | } 175 | #ifdef RLIM_NO_ATOMICS 176 | EntryList *e = PSC_Dictionary_get(self->entries, key, keysz); 177 | #else 178 | Entry *e = PSC_Dictionary_get(self->entries, key, keysz); 179 | #endif 180 | while (!e) 181 | { 182 | #ifdef RLIM_NO_ATOMICS 183 | EntryList *ne = PSC_malloc(sizeof *ne + 184 | self->nlimits * sizeof *ne->entries); 185 | pthread_mutex_init(&ne->lock, 0); 186 | ne->nlimits = self->nlimits; 187 | #else 188 | Entry *ne = PSC_malloc(self->nlimits * sizeof *ne); 189 | #endif 190 | memset(entries(ne), 0, self->nlimits * sizeof *entries(ne)); 191 | for (size_t i = 0; i < self->nlimits; ++i) 192 | { 193 | entries(ne)[i].last = ts.tv_sec / self->limits[i].res; 194 | } 195 | PSC_Dictionary_set(self->entries, key, keysz, ne, 0); 196 | e = PSC_Dictionary_get(self->entries, key, keysz); 197 | } 198 | #ifdef RLIM_NO_ATOMICS 199 | pthread_mutex_lock(&e->lock); 200 | #endif 201 | for (size_t i = 0; i < self->nlimits; ++i) 202 | { 203 | if (!checkLimit(self->limits + i, entries(e) + i, &ts)) ok = 0; 204 | } 205 | #ifdef RLIM_NO_ATOMICS 206 | pthread_mutex_unlock(&e->lock); 207 | #endif 208 | return ok; 209 | } 210 | 211 | void PSC_RateLimit_destroy(PSC_RateLimit *self) 212 | { 213 | if (!self) return; 214 | PSC_Dictionary_destroy(self->entries); 215 | free(self); 216 | } 217 | 218 | PSC_RateLimitOpts *PSC_RateLimitOpts_create(void) 219 | { 220 | PSC_RateLimitOpts *self = PSC_malloc(sizeof *self); 221 | memset(self, 0, sizeof *self); 222 | return self; 223 | } 224 | 225 | int PSC_RateLimitOpts_addLimit(PSC_RateLimitOpts *self, int seconds, int limit) 226 | { 227 | if (seconds < 1 || seconds > 0xffff || 228 | limit < 1 || limit > 0xffff) return -1; 229 | if (self->limits_count == self->limits_capa) 230 | { 231 | self->limits_capa += 8; 232 | self->limits = PSC_realloc(self->limits, 233 | self->limits_capa * sizeof *self->limits); 234 | } 235 | Limit *l = self->limits + self->limits_count++; 236 | l->seconds = seconds; 237 | l->limit = limit; 238 | l->res = (seconds + MAXCOUNTS - 1) / MAXCOUNTS; 239 | l->ncounts = (seconds + l->res - 1) / l->res; 240 | return 0; 241 | } 242 | 243 | int PSC_RateLimitOpts_equals(const PSC_RateLimitOpts *self, 244 | const PSC_RateLimitOpts *other) 245 | { 246 | if (self->limits_count != other->limits_count) return 0; 247 | for (size_t i = 0; i < self->limits_count; ++i) 248 | { 249 | if (self->limits[i].seconds != other->limits[i].seconds) return 0; 250 | if (self->limits[i].limit != other->limits[i].limit) return 0; 251 | } 252 | return 1; 253 | } 254 | 255 | void PSC_RateLimitOpts_destroy(PSC_RateLimitOpts *self) 256 | { 257 | if (!self) return; 258 | free(self->limits); 259 | free(self); 260 | } 261 | 262 | -------------------------------------------------------------------------------- /src/lib/core/resolver.c: -------------------------------------------------------------------------------- 1 | #define _DEFAULT_SOURCE 2 | 3 | #include 4 | 5 | #include "event.h" 6 | #include "ipaddr.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | struct PSC_Resolver 18 | { 19 | PSC_Event done; 20 | PSC_ThreadJob *job; 21 | PSC_List *entries; 22 | int handling; 23 | }; 24 | 25 | struct PSC_ResolverEntry 26 | { 27 | PSC_IpAddr *addr; 28 | char *name; 29 | }; 30 | 31 | static void deleteEntry(void *obj) 32 | { 33 | if (!obj) return; 34 | PSC_ResolverEntry *entry = obj; 35 | free(entry->name); 36 | PSC_IpAddr_destroy(entry->addr); 37 | free(entry); 38 | } 39 | 40 | SOEXPORT PSC_Resolver *PSC_Resolver_create(void) 41 | { 42 | PSC_Resolver *self = PSC_malloc(sizeof *self); 43 | PSC_Event_initStatic(&self->done, self); 44 | self->job = 0; 45 | self->entries = PSC_List_create(); 46 | self->handling = 0; 47 | return self; 48 | } 49 | 50 | SOEXPORT int PSC_Resolver_addAddr(PSC_Resolver *self, const PSC_IpAddr *addr) 51 | { 52 | if (self->job) return -1; 53 | PSC_ResolverEntry *entry = PSC_malloc(sizeof *entry); 54 | entry->addr = PSC_IpAddr_ref(addr); 55 | entry->name = 0; 56 | PSC_List_append(self->entries, entry, deleteEntry); 57 | return 0; 58 | } 59 | 60 | static void resolveProc(void *arg) 61 | { 62 | struct sockaddr_storage saddr; 63 | size_t saddrlen; 64 | char nbuf[NI_MAXHOST]; 65 | char sbuf[NI_MAXSERV]; 66 | 67 | PSC_Resolver *self = arg; 68 | 69 | PSC_ListIterator *i = PSC_List_iterator(self->entries); 70 | while (PSC_ListIterator_moveNext(i)) 71 | { 72 | PSC_ResolverEntry *entry = PSC_ListIterator_current(i); 73 | if (entry->addr && !entry->name) 74 | { 75 | if (PSC_IpAddr_sockAddr(entry->addr, 76 | (struct sockaddr *)&saddr) < 0) continue; 77 | if (PSC_IpAddr_proto(entry->addr) == PSC_P_IPv4) 78 | { 79 | saddrlen = sizeof (struct sockaddr_in); 80 | } 81 | else saddrlen = sizeof (struct sockaddr_in6); 82 | if (getnameinfo((struct sockaddr *)&saddr, saddrlen, nbuf, 83 | sizeof nbuf, sbuf, sizeof sbuf, 84 | NI_NAMEREQD|NI_NUMERICSERV) != 0) continue; 85 | if (PSC_ThreadJob_canceled()) break; 86 | entry->name = PSC_copystr(nbuf); 87 | } 88 | } 89 | PSC_ListIterator_destroy(i); 90 | } 91 | 92 | static void resolveDone(void *receiver, void *sender, void *args) 93 | { 94 | (void)sender; 95 | (void)args; 96 | 97 | PSC_Resolver *self = receiver; 98 | if (self->done.pool) 99 | { 100 | if (PSC_ThreadJob_hasCompleted(self->job)) 101 | { 102 | self->handling = 1; 103 | PSC_Event_raise(&self->done, 0, 0); 104 | if (self->handling < 0) 105 | { 106 | self->job = 0; 107 | self->handling = 0; 108 | PSC_Resolver_destroy(self); 109 | return; 110 | } 111 | self->handling = 0; 112 | } 113 | self->job = 0; 114 | } 115 | else 116 | { 117 | PSC_List_destroy(self->entries); 118 | free(self); 119 | } 120 | } 121 | 122 | SOEXPORT int PSC_Resolver_resolve(PSC_Resolver *self, int forceAsync) 123 | { 124 | if (self->job || PSC_List_size(self->entries) == 0) return -1; 125 | if (PSC_ThreadPool_active()) 126 | { 127 | self->job = PSC_ThreadJob_create(resolveProc, self, 0); 128 | PSC_Event_register(PSC_ThreadJob_finished(self->job), self, 129 | resolveDone, 0); 130 | if (PSC_ThreadPool_enqueue(self->job) < 0) 131 | { 132 | PSC_ThreadJob_destroy(self->job); 133 | self->job = 0; 134 | } 135 | } 136 | if (!self->job) 137 | { 138 | if (forceAsync) return -1; 139 | resolveProc(self); 140 | PSC_Event_raise(&self->done, 0, 0); 141 | } 142 | return 0; 143 | } 144 | 145 | SOEXPORT PSC_Event *PSC_Resolver_done(PSC_Resolver *self) 146 | { 147 | return &self->done; 148 | } 149 | 150 | SOEXPORT const PSC_List *PSC_Resolver_entries(const PSC_Resolver *self) 151 | { 152 | return self->entries; 153 | } 154 | 155 | SOEXPORT void PSC_Resolver_destroy(PSC_Resolver *self) 156 | { 157 | if (!self) return; 158 | if (self->handling) 159 | { 160 | self->handling = -1; 161 | return; 162 | } 163 | PSC_Event_destroyStatic(&self->done); 164 | if (self->job) 165 | { 166 | PSC_ThreadPool_cancel(self->job); 167 | return; 168 | } 169 | PSC_List_destroy(self->entries); 170 | free(self); 171 | } 172 | 173 | SOEXPORT const PSC_IpAddr *PSC_ResolverEntry_addr( 174 | const PSC_ResolverEntry *self) 175 | { 176 | return self->addr; 177 | } 178 | 179 | SOEXPORT const char *PSC_ResolverEntry_name(const PSC_ResolverEntry *self) 180 | { 181 | return self->name; 182 | } 183 | 184 | -------------------------------------------------------------------------------- /src/lib/core/runopts.c: -------------------------------------------------------------------------------- 1 | #include "runopts.h" 2 | 3 | #include 4 | 5 | static PSC_RunOpts opts; 6 | static int initialized; 7 | 8 | SOLOCAL PSC_RunOpts *runOpts(void) 9 | { 10 | if (!initialized) PSC_RunOpts_init(0); 11 | return &opts; 12 | } 13 | 14 | SOEXPORT void PSC_RunOpts_init(const char *pidfile) 15 | { 16 | opts.pidfile = pidfile; 17 | opts.uid = -1; 18 | opts.gid = -1; 19 | opts.daemonize = 1; 20 | opts.waitLaunched = 1; 21 | initialized = 1; 22 | } 23 | 24 | SOEXPORT void PSC_RunOpts_runas(long uid, long gid) 25 | { 26 | if (!initialized) PSC_RunOpts_init(0); 27 | opts.uid = uid; 28 | opts.gid = gid; 29 | } 30 | 31 | SOEXPORT void PSC_RunOpts_enableDefaultLogging(const char *logident) 32 | { 33 | if (!initialized) PSC_RunOpts_init(0); 34 | opts.logident = logident; 35 | opts.logEnabled = 1; 36 | } 37 | 38 | SOEXPORT void PSC_RunOpts_foreground(void) 39 | { 40 | if (!initialized) PSC_RunOpts_init(0); 41 | opts.daemonize = 0; 42 | } 43 | 44 | SOEXPORT void PSC_RunOpts_nowait(void) 45 | { 46 | if (!initialized) PSC_RunOpts_init(0); 47 | opts.waitLaunched = 0; 48 | } 49 | 50 | SOEXPORT void PSC_RunOpts_workerThreads(int workerThreads) 51 | { 52 | if (!initialized) PSC_RunOpts_init(0); 53 | opts.workerThreads = workerThreads; 54 | } 55 | -------------------------------------------------------------------------------- /src/lib/core/runopts.h: -------------------------------------------------------------------------------- 1 | #ifndef POSER_CORE_INT_RUNOPTS_H 2 | #define POSER_CORE_INT_RUNOPTS_H 3 | 4 | #include 5 | 6 | typedef struct PSC_RunOpts 7 | { 8 | const char *pidfile; 9 | const char *logident; 10 | long uid; 11 | long gid; 12 | int workerThreads; 13 | int daemonize; 14 | int waitLaunched; 15 | int logEnabled; 16 | } PSC_RunOpts; 17 | 18 | PSC_RunOpts *runOpts(void) ATTR_RETNONNULL; 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /src/lib/core/service.h: -------------------------------------------------------------------------------- 1 | #ifndef POSER_CORE_INT_SERVICE_H 2 | #define POSER_CORE_INT_SERVICE_H 3 | 4 | #include 5 | 6 | int PSC_Service_running(void); 7 | int PSC_Service_shutsdown(void); 8 | 9 | #ifdef HAVE_EVPORTS 10 | # undef HAVE_KQUEUE 11 | int PSC_Service_epfd(void); 12 | #endif 13 | 14 | #ifdef HAVE_KQUEUE 15 | void PSC_Service_armTimer(void *timer, unsigned ms, int periodic); 16 | void PSC_Service_unarmTimer(void *timer, unsigned ms, int periodic); 17 | #endif 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /src/lib/core/sharedobj.c: -------------------------------------------------------------------------------- 1 | #include "sharedobj.h" 2 | 3 | #ifndef NO_SHAREDOBJ 4 | # include 5 | # include 6 | 7 | static atomic_uint intEpoch; 8 | static atomic_uint intDestroy; 9 | static atomic_uint epoch; 10 | static atomic_uint nthr; 11 | static atomic_uint *res; 12 | 13 | static THREADLOCAL unsigned tid; 14 | static THREADLOCAL unsigned ncreated; 15 | static THREADLOCAL unsigned nretired; 16 | static THREADLOCAL SharedObj *retired; 17 | 18 | SOLOCAL void *SharedObj_create(size_t sz, void (*destroy)(void *)) 19 | { 20 | unsigned created; 21 | if (++ncreated == atomic_load_explicit(&intEpoch, memory_order_relaxed)) 22 | { 23 | ncreated = 0; 24 | created = atomic_fetch_add_explicit(&epoch, 1, memory_order_acq_rel); 25 | } 26 | else created = atomic_load_explicit(&epoch, memory_order_acquire); 27 | SharedObj *self = PSC_malloc(sz); 28 | self->destroy = destroy; 29 | atomic_store_explicit(&self->next, 0, memory_order_release); 30 | atomic_store_explicit(&self->created, created, memory_order_release); 31 | atomic_store_explicit(&self->retired, created - 1U, memory_order_release); 32 | return self; 33 | } 34 | 35 | SOLOCAL void SharedObj_retire(void *self) 36 | { 37 | SharedObj *so = self; 38 | atomic_store_explicit(&so->retired, atomic_load_explicit(&epoch, 39 | memory_order_consume), memory_order_relaxed); 40 | atomic_store_explicit(&so->next, retired, memory_order_relaxed); 41 | retired = so; 42 | if (++nretired == atomic_load_explicit(&intDestroy, memory_order_relaxed)) 43 | { 44 | nretired = 0; 45 | unsigned nt = atomic_load_explicit(&nthr, memory_order_relaxed); 46 | for (SharedObj *i = retired, *p = 0, *n = 0; i; i = n) 47 | { 48 | n = atomic_load_explicit(&i->next, memory_order_relaxed); 49 | unsigned creat = atomic_load_explicit(&i->created, 50 | memory_order_relaxed); 51 | unsigned retd = atomic_load_explicit(&i->retired, 52 | memory_order_relaxed); 53 | int conflicts = 0; 54 | for (unsigned t = 0; t < nt; ++t) 55 | { 56 | unsigned tres = atomic_load_explicit(res + t, 57 | memory_order_consume); 58 | if (creat > retd ? 59 | (tres >= creat || tres <= retd) : 60 | (tres >= creat && tres <= retd)) 61 | { 62 | conflicts = 1; 63 | break; 64 | } 65 | } 66 | if (conflicts) 67 | { 68 | p = i; 69 | } 70 | else 71 | { 72 | if (p) atomic_store_explicit(&p->next, n, 73 | memory_order_relaxed); 74 | else retired = n; 75 | if (i->destroy) i->destroy(i); 76 | else free(i); 77 | } 78 | } 79 | } 80 | } 81 | 82 | SOLOCAL void SOM_registerThread(void) 83 | { 84 | tid = atomic_fetch_add_explicit(&nthr, 1, memory_order_acq_rel); 85 | } 86 | 87 | SOLOCAL void SOM_init(unsigned nthreads, 88 | unsigned epochInterval, unsigned destroyInterval) 89 | { 90 | atomic_store_explicit(&intEpoch, epochInterval, memory_order_release); 91 | atomic_store_explicit(&intDestroy, destroyInterval, memory_order_release); 92 | res = PSC_malloc(nthreads * sizeof *res); 93 | } 94 | 95 | SOLOCAL void *SOM_reserve(void *_Atomic *ref) 96 | { 97 | for (;;) 98 | { 99 | unsigned ep = atomic_load_explicit(&epoch, memory_order_consume); 100 | atomic_store_explicit(res + tid, ep, memory_order_release); 101 | void *p = atomic_load_explicit(ref, memory_order_acquire); 102 | if (ep == atomic_load_explicit(&epoch, memory_order_consume)) 103 | { 104 | return p; 105 | } 106 | } 107 | } 108 | 109 | SOLOCAL void SOM_release(void) 110 | { 111 | unsigned ep = atomic_load_explicit(&epoch, memory_order_consume); 112 | atomic_store_explicit(res + tid, ep - 1U, memory_order_release); 113 | } 114 | 115 | #else 116 | typedef int posercore___dummy; 117 | #endif 118 | -------------------------------------------------------------------------------- /src/lib/core/sharedobj.h: -------------------------------------------------------------------------------- 1 | #ifndef POSER_CORE_INT_SHAREDOBJ_H 2 | #define POSER_CORE_INT_SHAREDOBJ_H 3 | 4 | #undef NO_SHAREDOBJ 5 | #if defined(NO_ATOMICS) || defined(__STDC_NO_ATOMICS__) 6 | # define NO_SHAREDOBJ 7 | #else 8 | # include 9 | # if ATOMIC_POINTER_LOCK_FREE != 2 10 | # define NO_SHAREDOBJ 11 | # endif 12 | #endif 13 | 14 | #ifndef NO_SHAREDOBJ 15 | # include 16 | # include 17 | 18 | C_CLASS_DECL(SharedObj); 19 | 20 | struct SharedObj 21 | { 22 | void (*destroy)(void *); 23 | SharedObj *_Atomic next; 24 | atomic_uint created; 25 | atomic_uint retired; 26 | }; 27 | 28 | void *SharedObj_create(size_t sz, void (*destroy)(void *)); 29 | void SharedObj_retire(void *self); 30 | 31 | void SOM_init(unsigned nthreads, 32 | unsigned epochInterval, unsigned destroyInterval); 33 | void SOM_registerThread(void); 34 | void *SOM_reserve(void *_Atomic *ref); 35 | void SOM_release(void); 36 | 37 | #else 38 | 39 | #define SOM_registerThread() 40 | 41 | #endif 42 | #endif 43 | -------------------------------------------------------------------------------- /src/lib/core/stackmgr.c: -------------------------------------------------------------------------------- 1 | #undef STACK_MFLAGS 2 | #if defined(HAVE_MANON) || defined(HAVE_MANONYMOUS) 3 | # define _DEFAULT_SOURCE 4 | # ifdef HAVE_MSTACK 5 | # ifdef HAVE_MANON 6 | # define STACK_MFLAGS (MAP_ANON|MAP_PRIVATE|MAP_STACK) 7 | # else 8 | # define STACK_MFLAGS (MAP_ANONYMOUS|MAP_PRIVATE|MAP_STACK) 9 | # endif 10 | # else 11 | # ifdef HAVE_MANON 12 | # define STACK_MFLAGS (MAP_ANON|MAP_PRIVATE) 13 | # else 14 | # define STACK_MFLAGS (MAP_ANONYMOUS|MAP_PRIVATE) 15 | # endif 16 | # endif 17 | #endif 18 | 19 | #include "stackmgr.h" 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #ifdef STACK_MFLAGS 26 | # include 27 | # include 28 | #endif 29 | 30 | #define STACKSCHUNK 16 31 | 32 | static size_t sz = 2U * 1024U * 1024U; 33 | 34 | static void **stacks; 35 | static size_t nstacks; 36 | static size_t cstacks; 37 | static pthread_mutex_t stackslock = PTHREAD_MUTEX_INITIALIZER; 38 | 39 | SOLOCAL int StackMgr_setSize(size_t stacksz) 40 | { 41 | if (stacks) return -1; 42 | if (stacksz < 64U * 1024U) stacksz = 64U * 1024U; 43 | if (stacksz > 16U * 1024U * 1024U) stacksz = 16U * 1024U * 1024U; 44 | sz = stacksz; 45 | return 0; 46 | } 47 | 48 | SOLOCAL size_t StackMgr_size(void) 49 | { 50 | return sz; 51 | } 52 | 53 | SOLOCAL void *StackMgr_getStack(void) 54 | { 55 | void *stack = 0; 56 | pthread_mutex_lock(&stackslock); 57 | if (nstacks) stack = stacks[--nstacks]; 58 | pthread_mutex_unlock(&stackslock); 59 | if (stack) return stack; 60 | #ifdef STACK_MFLAGS 61 | stack = mmap(0, sz, PROT_READ|PROT_WRITE, STACK_MFLAGS, -1, 0); 62 | if (stack == MAP_FAILED) PSC_Service_panic("stack allocation failed."); 63 | #else 64 | stack = PSC_malloc(sz); 65 | #endif 66 | return stack; 67 | } 68 | 69 | SOLOCAL void StackMgr_returnStack(void *stack) 70 | { 71 | if (!stack) return; 72 | pthread_mutex_lock(&stackslock); 73 | if (nstacks == cstacks) 74 | { 75 | cstacks += STACKSCHUNK; 76 | stacks = PSC_realloc(stacks, cstacks * sizeof *stacks); 77 | } 78 | stacks[nstacks++] = stack; 79 | #if defined(STACK_MFLAGS) && defined(HAVE_MADVISE) && defined(HAVE_MADVFREE) 80 | madvise(stack, sz, MADV_FREE); 81 | #endif 82 | pthread_mutex_unlock(&stackslock); 83 | } 84 | 85 | SOLOCAL void StackMgr_clean(void) 86 | { 87 | for (size_t i = 0; i < nstacks; ++i) 88 | { 89 | #ifdef STACK_MFLAGS 90 | munmap(stacks[i], sz); 91 | #else 92 | free(stacks[i]); 93 | #endif 94 | } 95 | free(stacks); 96 | nstacks = 0; 97 | cstacks = 0; 98 | } 99 | 100 | -------------------------------------------------------------------------------- /src/lib/core/stackmgr.h: -------------------------------------------------------------------------------- 1 | #ifndef POSER_CORE_INT_STACKMGR_H 2 | #define POSER_CORE_INT_STACKMGR_H 3 | 4 | #include 5 | #include 6 | 7 | int StackMgr_setSize(size_t stacksz); 8 | size_t StackMgr_size(void); 9 | void *StackMgr_getStack(void) ATTR_RETNONNULL; 10 | void StackMgr_returnStack(void *stack); 11 | void StackMgr_clean(void); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /src/lib/core/stringbuilder.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #define SBCHUNKSZ 512 9 | 10 | struct PSC_StringBuilder 11 | { 12 | size_t size; 13 | size_t capa; 14 | char *str; 15 | }; 16 | 17 | SOEXPORT PSC_StringBuilder *PSC_StringBuilder_create(void) 18 | { 19 | PSC_StringBuilder *self = PSC_malloc(sizeof *self); 20 | memset(self, 0, sizeof *self); 21 | return self; 22 | } 23 | 24 | SOEXPORT void PSC_StringBuilder_append( 25 | PSC_StringBuilder *self, const char *str) 26 | { 27 | size_t newsz = self->size + strlen(str); 28 | if (self->capa <= newsz) 29 | { 30 | while (self->capa <= newsz) self->capa += SBCHUNKSZ; 31 | self->str = PSC_realloc(self->str, self->capa); 32 | } 33 | strcpy(self->str + self->size, str); 34 | self->size = newsz; 35 | } 36 | 37 | SOEXPORT void PSC_StringBuilder_appendChar( 38 | PSC_StringBuilder *self, char c) 39 | { 40 | char str[] = {c, 0}; 41 | PSC_StringBuilder_append(self, str); 42 | } 43 | 44 | SOEXPORT const char *PSC_StringBuilder_str(const PSC_StringBuilder *self) 45 | { 46 | return self->str; 47 | } 48 | 49 | SOEXPORT void PSC_StringBuilder_destroy(PSC_StringBuilder *self) 50 | { 51 | if (!self) return; 52 | free(self->str); 53 | free(self); 54 | } 55 | 56 | -------------------------------------------------------------------------------- /src/lib/core/threadpool.h: -------------------------------------------------------------------------------- 1 | #ifndef POSER_CORE_INT_THREADPOOL_H 2 | #define POSER_CORE_INT_THREADPOOL_H 3 | 4 | #include 5 | 6 | int PSC_ThreadPool_nthreads(void); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /src/lib/core/timer.h: -------------------------------------------------------------------------------- 1 | #ifndef POSER_CORE_INT_TIMER_H 2 | #define POSER_CORE_INT_TIMER_H 3 | 4 | #include 5 | 6 | #if defined(HAVE_EVPORTS) || defined(HAVE_KQUEUE) 7 | void PSC_Timer_doexpire(PSC_Timer *self) CMETHOD; 8 | #elif !defined(HAVE_TIMERFD) 9 | void PSC_Timer_underrun(void); 10 | #endif 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /src/lib/core/util.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | SOEXPORT void *PSC_malloc(size_t size) 10 | { 11 | void *m = malloc(size); 12 | if (!m) PSC_Service_panic("memory allocation failed."); 13 | return m; 14 | } 15 | 16 | SOEXPORT void *PSC_realloc(void *ptr, size_t size) 17 | { 18 | void *m = realloc(ptr, size); 19 | if (!m) PSC_Service_panic("memory allocation failed."); 20 | return m; 21 | } 22 | 23 | SOEXPORT char *PSC_copystr(const char *src) 24 | { 25 | if (!src) return 0; 26 | char *copy = PSC_malloc(strlen(src) + 1); 27 | strcpy(copy, src); 28 | return copy; 29 | } 30 | 31 | SOEXPORT char *PSC_lowerstr(const char *src) 32 | { 33 | char *lower = PSC_copystr(src); 34 | char *p = lower; 35 | if (p) while (*p) 36 | { 37 | *p = tolower((unsigned char)*p); 38 | ++p; 39 | } 40 | return lower; 41 | } 42 | 43 | SOEXPORT char *PSC_joinstr(const char *delim, char **strings) 44 | { 45 | int n = 0; 46 | size_t rlen = 0; 47 | size_t dlen = strlen(delim); 48 | char **cur; 49 | for (cur = strings; *cur; ++cur) 50 | { 51 | ++n; 52 | rlen += strlen(*cur); 53 | } 54 | if (!n) return 0; 55 | if (n > 1) 56 | { 57 | rlen += (n - 1) * dlen; 58 | } 59 | char *joined = PSC_malloc(rlen + 1); 60 | strcpy(joined, *strings); 61 | char *w = joined + strlen(*strings); 62 | cur = strings+1; 63 | while (*cur) 64 | { 65 | strcpy(w, delim); 66 | w += dlen; 67 | strcpy(w, *cur); 68 | w += strlen(*cur); 69 | ++cur; 70 | } 71 | return joined; 72 | } 73 | 74 | SOEXPORT const char *PSC_basename(const char *path) 75 | { 76 | size_t seppos; 77 | while (*path && path[(seppos = strcspn(path, "/"))]) path += seppos + 1; 78 | if (*path) return path; 79 | return "."; 80 | } 81 | 82 | -------------------------------------------------------------------------------- /src/lib/core/xxhash.c: -------------------------------------------------------------------------------- 1 | #ifdef NDEBUG 2 | # pragma GCC visibility push (hidden) 3 | # define POSER_XXHASH_HIDDEN 4 | #endif 5 | #define XXH_NO_STDLIB 6 | #define XXH_NO_STREAM 7 | #include "contrib/xxHash/xxhash.c" 8 | #ifdef POSER_XXHASH_HIDDEN 9 | # pragma GCC visibility pop 10 | #endif 11 | -------------------------------------------------------------------------------- /src/lib/core/xxhash.h: -------------------------------------------------------------------------------- 1 | #ifndef POSER_CORE_INT_XXHASH_H 2 | #define POSER_CORE_INT_XXHASH_H 3 | 4 | #include 5 | 6 | #include "contrib/xxHash/xxhash.h" 7 | #ifdef HAVE_XXHX86 8 | # include "contrib/xxHash/xxh_x86dispatch.h" 9 | #endif 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /src/lib/core/xxhx86.c: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_XXHX86 2 | # ifdef NDEBUG 3 | # pragma GCC visibility push (hidden) 4 | # define POSER_XXHX86_HIDDEN 5 | # endif 6 | # include "contrib/xxHash/xxh_x86dispatch.c" 7 | # ifdef POSER_XXHX86_HIDDEN 8 | # pragma GCC visibility pop 9 | # endif 10 | #else 11 | # include 12 | SOLOCAL char psc__dummy; 13 | #endif 14 | --------------------------------------------------------------------------------