Compare commits
234 Commits
Author | SHA1 | Date |
---|---|---|
Philippe Pittoli | 83d5374586 | |
Philippe Pittoli | 9d875062f6 | |
Karchnu | b75e7dbbdc | |
Philippe Pittoli | d26d6de759 | |
Philippe Pittoli | 7ab2fccfd6 | |
Philippe Pittoli | 0b96aaafeb | |
Philippe Pittoli | 136b294aac | |
Philippe Pittoli | 6a28fa8ac7 | |
Philippe Pittoli | 7d96ec3c28 | |
Philippe Pittoli | 7ef4c43d60 | |
Philippe Pittoli | 49e0430fb5 | |
Philippe Pittoli | 1479d4e243 | |
Philippe Pittoli | f0e41455be | |
Philippe Pittoli | 81cf462e10 | |
Philippe Pittoli | c394c854f4 | |
Philippe Pittoli | 9d69afe3ae | |
Philippe Pittoli | a6e1609fe6 | |
Philippe Pittoli | 48a727bf19 | |
Philippe Pittoli | 94dfefe198 | |
Philippe Pittoli | 4ed4981e42 | |
Philippe Pittoli | 0c8fc284d3 | |
Philippe Pittoli | 745b8c12ca | |
Philippe Pittoli | 007d329f83 | |
Philippe Pittoli | 6564f934e1 | |
Philippe Pittoli | 9fabcbdc3c | |
Philippe Pittoli | 247134b178 | |
Philippe Pittoli | d72d85294a | |
Philippe Pittoli | 37b460d52d | |
Philippe Pittoli | 8c168fdbf8 | |
Philippe Pittoli | bdff3156f4 | |
Philippe Pittoli | 61186c9ea9 | |
Philippe Pittoli | bf4d5c803f | |
Philippe Pittoli | 2b99e18046 | |
Philippe Pittoli | 6a912202e4 | |
Philippe Pittoli | 1c850be4cf | |
Philippe Pittoli | ddc1d65bef | |
Philippe Pittoli | 08523fa90a | |
Philippe Pittoli | dda7514a61 | |
Philippe Pittoli | afd8bbec18 | |
Philippe Pittoli | db5cf0cbf0 | |
Philippe Pittoli | e849fdafda | |
Philippe Pittoli | 49698f11bd | |
Philippe Pittoli | f79938f441 | |
Philippe Pittoli | 7a1e66423f | |
Philippe Pittoli | 0ad272d10a | |
Philippe Pittoli | 2192f44ffc | |
Philippe Pittoli | bfdbacb981 | |
Philippe Pittoli | db561d99b5 | |
Philippe Pittoli | 79752068c6 | |
Philippe Pittoli | 4b0ca0a59d | |
Philippe Pittoli | 32fa98bc76 | |
Philippe Pittoli | d2ab260255 | |
Philippe Pittoli | 7904d5eef6 | |
Philippe Pittoli | af709a665c | |
Philippe Pittoli | 1269b55c05 | |
Philippe Pittoli | 23de66befc | |
Philippe Pittoli | 5ee0a6e6e0 | |
Philippe Pittoli | 0a836c56cd | |
Philippe Pittoli | 0356540e7b | |
Philippe Pittoli | a958666060 | |
Philippe Pittoli | ae99353c19 | |
Philippe Pittoli | 12123b0cb4 | |
Philippe Pittoli | b0cf9638ec | |
Philippe Pittoli | 51cad2c07d | |
Philippe Pittoli | 21ecf157d1 | |
Philippe Pittoli | 368513bca5 | |
Philippe Pittoli | 9696190e99 | |
Philippe Pittoli | e6ad0ce65c | |
Philippe Pittoli | 6fa8f31dd4 | |
Philippe Pittoli | eb99ba6cb2 | |
Philippe Pittoli | 3e3d996e7b | |
Philippe Pittoli | da27ce33dd | |
Philippe Pittoli | 0eb93c805d | |
Philippe Pittoli | ae62d300ff | |
Philippe Pittoli | 4651430ef8 | |
Philippe Pittoli | fd63139157 | |
Philippe Pittoli | 82f81a7fb7 | |
Philippe Pittoli | 06a01f2b4b | |
Philippe Pittoli | 132743442b | |
Philippe Pittoli | a0c446ca28 | |
Philippe Pittoli | b2bf66c436 | |
Philippe Pittoli | 0feb31b4c7 | |
Philippe Pittoli | cab07003ad | |
Philippe Pittoli | 273204af6d | |
Philippe Pittoli | b59ad86e41 | |
Philippe Pittoli | b111982a0f | |
Philippe Pittoli | 3123051ef0 | |
Philippe Pittoli | 9dc4cfa003 | |
Philippe Pittoli | 2befab21e2 | |
Philippe Pittoli | 9462224255 | |
Philippe Pittoli | 3695a8ec82 | |
Philippe Pittoli | 6601eb61b0 | |
Philippe Pittoli | f07b915124 | |
Philippe Pittoli | eafdc3749a | |
Philippe Pittoli | 66ddeb2207 | |
Philippe Pittoli | caf255d6c4 | |
Philippe Pittoli | 3514b4fe96 | |
Philippe Pittoli | 13a60d0158 | |
Philippe Pittoli | e80e99d47f | |
Philippe Pittoli | ed9cd24b22 | |
Philippe Pittoli | 178f205d44 | |
Philippe Pittoli | 5b73186353 | |
Philippe Pittoli | c8f34ef3c2 | |
Philippe Pittoli | 472dd1f1ab | |
Philippe Pittoli | 71aa496501 | |
Philippe Pittoli | 125a960816 | |
Philippe Pittoli | a8224c1cf9 | |
Philippe Pittoli | a6cfa88f79 | |
Philippe Pittoli | c5e3b7b901 | |
Philippe Pittoli | 8ea70b51ee | |
Philippe Pittoli | d585ffb8ee | |
Philippe Pittoli | 32fd31934c | |
Philippe Pittoli | aca3f2183d | |
Philippe Pittoli | 614e972b95 | |
Philippe Pittoli | f3c7695462 | |
Philippe Pittoli | bb9d397a40 | |
Philippe Pittoli | 1034b1aa5c | |
Philippe Pittoli | ba6671d902 | |
Philippe Pittoli | 28d7dd8fc2 | |
Philippe Pittoli | 58320cbb46 | |
Philippe Pittoli | 1e3c1b2625 | |
Philippe Pittoli | 871b2b249c | |
Philippe Pittoli | b73550bdcf | |
Philippe Pittoli | a0e9515600 | |
Philippe Pittoli | 5ad00d0675 | |
Philippe Pittoli | 45f3fa1860 | |
Philippe Pittoli | 506bd21d57 | |
Philippe Pittoli | 0cf2e5ef1f | |
Philippe Pittoli | 978676051a | |
Philippe Pittoli | ec787d7496 | |
Philippe Pittoli | fb12f65218 | |
Philippe Pittoli | 49089b910b | |
Philippe Pittoli | 2a714cd064 | |
Philippe Pittoli | d99a8d13e3 | |
Philippe Pittoli | 4bbd5fc686 | |
Philippe Pittoli | 49b1b3bab2 | |
Philippe Pittoli | 1a6c13c85d | |
Philippe Pittoli | ce28b72f3b | |
Philippe Pittoli | 9ea49087bd | |
Philippe Pittoli | d72aac1a50 | |
Philippe Pittoli | 1b19118701 | |
Philippe Pittoli | 54fc5aa9e0 | |
Philippe Pittoli | 6b8d659319 | |
Philippe Pittoli | 3097dca06a | |
Philippe Pittoli | fc0c28fb7e | |
Philippe Pittoli | 4421ee31c4 | |
Philippe Pittoli | bbf7e669ff | |
Philippe Pittoli | 78670e5b71 | |
Philippe Pittoli | b43fd57704 | |
Philippe Pittoli | 14509b8d28 | |
Philippe Pittoli | 4fac81b143 | |
Philippe Pittoli | b50d910906 | |
Philippe Pittoli | 8012cff4bf | |
Philippe Pittoli | 266f1daaad | |
Philippe Pittoli | 2ad505b305 | |
Philippe Pittoli | 79f9fdc3e2 | |
Philippe Pittoli | 8a347f9ece | |
Philippe Pittoli | c77ae35751 | |
Philippe Pittoli | 05a47b8473 | |
Philippe Pittoli | 4b0778e37d | |
Philippe Pittoli | de1d221881 | |
Philippe Pittoli | 98eede6814 | |
Karchnu | 1a161d1b14 | |
Philippe Pittoli | e7c1c8b96d | |
Philippe Pittoli | c7f48d21e4 | |
Philippe Pittoli | 9eea1dbc07 | |
Philippe Pittoli | 727de2988f | |
Philippe Pittoli | d52fbdf61d | |
Philippe Pittoli | 6819de1da5 | |
Philippe Pittoli | 9f214180a7 | |
Philippe Pittoli | 1f5ac951cb | |
Philippe Pittoli | bc0fe07990 | |
Philippe Pittoli | 1a83b3c824 | |
Philippe Pittoli | 168bea7e78 | |
Philippe Pittoli | 8b10612456 | |
Philippe Pittoli | ca0d6adbc6 | |
Philippe Pittoli | ceafe4c84f | |
Philippe Pittoli | 200219d2fe | |
Philippe Pittoli | 9d1fef34a6 | |
Philippe Pittoli | 9321158a22 | |
Philippe Pittoli | 72ad635874 | |
Philippe Pittoli | 91995657dd | |
Philippe Pittoli | 0eb5dc57f5 | |
Philippe Pittoli | 3b7203c58d | |
Philippe Pittoli | 6038a277f3 | |
Philippe Pittoli | 0e2043c5e6 | |
Philippe Pittoli | a39ce64b7b | |
Philippe Pittoli | 2bb06db137 | |
Philippe Pittoli | 51e10d7f1e | |
Philippe Pittoli | 29d18e8ca1 | |
Philippe Pittoli | 03b1222ff0 | |
Philippe Pittoli | 1c26a69acd | |
Philippe Pittoli | 0fddc05576 | |
Philippe Pittoli | 1c8be3390e | |
Philippe Pittoli | 7204ade9e3 | |
Philippe Pittoli | 444078fcc6 | |
Philippe Pittoli | 382dc06f85 | |
Philippe Pittoli | 382dcc07d7 | |
Philippe Pittoli | a0dbd66fd2 | |
Philippe Pittoli | 9011578d8b | |
Philippe Pittoli | 1762f50100 | |
Philippe Pittoli | 69732ccad8 | |
Philippe Pittoli | 87f6f9071b | |
Philippe Pittoli | c14148ef35 | |
Philippe Pittoli | 9d16d6f2b8 | |
Philippe Pittoli | fc4899a26f | |
Philippe Pittoli | 13e7619899 | |
Philippe Pittoli | 33f7c9ccfb | |
Philippe Pittoli | e6edfd0e43 | |
Karchnu | 513348652e | |
Karchnu | bf600e0889 | |
Karchnu | f9b9000a3f | |
Karchnu | 1439bb78ca | |
Karchnu | c8757d0fb0 | |
Karchnu | 7cb5d2669c | |
Karchnu | 7caa934753 | |
Karchnu | 62db8ff7fd | |
Karchnu | d32e26b848 | |
Karchnu | a99d5317b0 | |
Karchnu | 825e0c1b2c | |
Karchnu | c69ce64273 | |
Karchnu | 7659766fc0 | |
Karchnu | b4cc1814cd | |
Karchnu | ec33e6086e | |
Karchnu | 556652418a | |
Karchnu | 7eeda65cd9 | |
Karchnu | da78f0acfd | |
Karchnu | fd04cc0f83 | |
Karchnu | 62dcda8263 | |
Karchnu | 7fda2bd1ad | |
Karchnu | d9d927cfd2 | |
Karchnu | c2aa94716f | |
Karchnu | e147379f06 | |
Karchnu | b8c5c9d70e |
|
@ -6,3 +6,4 @@
|
|||
*.bin
|
||||
*.dSYM
|
||||
drop/
|
||||
pres/
|
||||
|
|
68
Makefile
68
Makefile
|
@ -1,5 +1,5 @@
|
|||
PACKAGE = 'libipc'
|
||||
VERSION = '0.6.0'
|
||||
VERSION = '0.7.2'
|
||||
|
||||
PREFIX := /usr/local
|
||||
BINDIR := $(PREFIX)/bin
|
||||
|
@ -45,38 +45,38 @@ man/libipc.7.uninstall:
|
|||
@echo '[01;37m RM > [01;37m$(MANDIR)/man7/libipc.7[00m'
|
||||
$(Q)rm -f '$(DESTDIR)$(MANDIR)/man7/libipc.7'
|
||||
|
||||
libipc.so: src/communication.o src/error.o src/fs.o src/message.o src/network.o src/print.o src/usocket.o src/utils.o src/fs.h src/ipc.h src/message.h src/usocket.h src/utils.h
|
||||
libipc.so: src/communication.o src/context.o src/error.o src/fs.o src/message.o src/network.o src/print.o src/service_path.o src/usocket.o src/utils.o src/fs.h src/ipc.h src/message.h src/usocket.h src/utils.h
|
||||
@echo '[01;32m LD > [01;37mlibipc.so[00m'
|
||||
$(Q)$(CC) -o libipc.so -shared $(LDFLAGS) src/communication.o src/error.o src/fs.o src/message.o src/network.o src/print.o src/usocket.o src/utils.o
|
||||
$(Q)$(CC) -o libipc.so -shared $(LDFLAGS) src/communication.o src/context.o src/error.o src/fs.o src/message.o src/network.o src/print.o src/service_path.o src/usocket.o src/utils.o
|
||||
|
||||
libipc.so.install: libipc.so
|
||||
@echo '[01;31m IN > [01;37m$(LIBDIR)/libipc.so.0.6.0[00m'
|
||||
@echo '[01;31m IN > [01;37m$(LIBDIR)/libipc.so.0.7.2[00m'
|
||||
$(Q)mkdir -p '$(DESTDIR)$(LIBDIR)'
|
||||
$(Q)install -m0755 libipc.so $(DESTDIR)$(LIBDIR)/libipc.so.0.6.0
|
||||
@echo '[01;35m LN > [01;37m$(LIBDIR)/libipc.so.0.6[00m'
|
||||
$(Q)ln -sf '$(LIBDIR)/libipc.so.0.6.0' '$(DESTDIR)/$(LIBDIR)/libipc.so.0.6'
|
||||
$(Q)install -m0755 libipc.so $(DESTDIR)$(LIBDIR)/libipc.so.0.7.2
|
||||
@echo '[01;35m LN > [01;37m$(LIBDIR)/libipc.so.0.7[00m'
|
||||
$(Q)ln -sf '$(LIBDIR)/libipc.so.0.7.2' '$(DESTDIR)/$(LIBDIR)/libipc.so.0.7'
|
||||
@echo '[01;35m LN > [01;37m$(LIBDIR)/libipc.so.0[00m'
|
||||
$(Q)ln -sf '$(LIBDIR)/libipc.so.0.6.0' '$(DESTDIR)/$(LIBDIR)/libipc.so.0'
|
||||
$(Q)ln -sf '$(LIBDIR)/libipc.so.0.7.2' '$(DESTDIR)/$(LIBDIR)/libipc.so.0'
|
||||
@echo '[01;35m LN > [01;37m$(LIBDIR)/libipc.so[00m'
|
||||
$(Q)ln -sf '$(LIBDIR)/libipc.so.0.6.0' '$(DESTDIR)/$(LIBDIR)/libipc.so'
|
||||
$(Q)ln -sf '$(LIBDIR)/libipc.so.0.7.2' '$(DESTDIR)/$(LIBDIR)/libipc.so'
|
||||
|
||||
libipc.so.clean:
|
||||
@echo '[01;37m RM > [01;37mlibipc.so[00m'
|
||||
$(Q)rm -f libipc.so
|
||||
|
||||
libipc.so.uninstall:
|
||||
@echo '[01;37m RM > [01;37m$(LIBDIR)/libipc.so.0.6.0[00m'
|
||||
$(Q)rm -f '$(DESTDIR)$(LIBDIR)/libipc.so.0.6.0'
|
||||
@echo '[01;37m RM > [01;37m$(LIBDIR)/libipc.so.0.6[00m'
|
||||
$(Q)rm -f '$(DESTDIR)$(LIBDIR)/libipc.so.0.6'
|
||||
@echo '[01;37m RM > [01;37m$(LIBDIR)/libipc.so.0.7.2[00m'
|
||||
$(Q)rm -f '$(DESTDIR)$(LIBDIR)/libipc.so.0.7.2'
|
||||
@echo '[01;37m RM > [01;37m$(LIBDIR)/libipc.so.0.7[00m'
|
||||
$(Q)rm -f '$(DESTDIR)$(LIBDIR)/libipc.so.0.7'
|
||||
@echo '[01;37m RM > [01;37m$(LIBDIR)/libipc.so.0[00m'
|
||||
$(Q)rm -f '$(DESTDIR)$(LIBDIR)/libipc.so.0'
|
||||
@echo '[01;37m RM > [01;37m$(LIBDIR)/libipc.so[00m'
|
||||
$(Q)rm -f '$(DESTDIR)$(LIBDIR)/libipc.so'
|
||||
|
||||
libipc.a: src/communication.o src/error.o src/fs.o src/message.o src/network.o src/print.o src/usocket.o src/utils.o src/fs.h src/ipc.h src/message.h src/usocket.h src/utils.h
|
||||
libipc.a: src/communication.o src/context.o src/error.o src/fs.o src/message.o src/network.o src/print.o src/service_path.o src/usocket.o src/utils.o src/fs.h src/ipc.h src/message.h src/usocket.h src/utils.h
|
||||
@echo '[01;32m LD > [01;37mlibipc.a[00m'
|
||||
$(Q)$(AR) rc 'libipc.a' src/communication.o src/error.o src/fs.o src/message.o src/network.o src/print.o src/usocket.o src/utils.o
|
||||
$(Q)$(AR) rc 'libipc.a' src/communication.o src/context.o src/error.o src/fs.o src/message.o src/network.o src/print.o src/service_path.o src/usocket.o src/utils.o
|
||||
|
||||
libipc.a.install: libipc.a
|
||||
@echo '[01;31m IN > [01;37m$(LIBDIR)/libipc.a[00m'
|
||||
|
@ -103,6 +103,18 @@ src/communication.o.clean:
|
|||
|
||||
src/communication.o.uninstall:
|
||||
|
||||
src/context.o: src/context.c src/ipc.h
|
||||
@echo '[01;34m CC > [01;37msrc/context.o[00m'
|
||||
$(Q)$(CC) $(CFLAGS) -fPIC -std=c11 -c src/context.c -fPIC -std=c11 -o src/context.o
|
||||
|
||||
src/context.o.install:
|
||||
|
||||
src/context.o.clean:
|
||||
@echo '[01;37m RM > [01;37msrc/context.o[00m'
|
||||
$(Q)rm -f src/context.o
|
||||
|
||||
src/context.o.uninstall:
|
||||
|
||||
src/error.o: src/error.c src/ipc.h
|
||||
@echo '[01;34m CC > [01;37msrc/error.o[00m'
|
||||
$(Q)$(CC) $(CFLAGS) -fPIC -std=c11 -c src/error.c -fPIC -std=c11 -o src/error.o
|
||||
|
@ -163,6 +175,18 @@ src/print.o.clean:
|
|||
|
||||
src/print.o.uninstall:
|
||||
|
||||
src/service_path.o: src/service_path.c src/ipc.h
|
||||
@echo '[01;34m CC > [01;37msrc/service_path.o[00m'
|
||||
$(Q)$(CC) $(CFLAGS) -fPIC -std=c11 -c src/service_path.c -fPIC -std=c11 -o src/service_path.o
|
||||
|
||||
src/service_path.o.install:
|
||||
|
||||
src/service_path.o.clean:
|
||||
@echo '[01;37m RM > [01;37msrc/service_path.o[00m'
|
||||
$(Q)rm -f src/service_path.o
|
||||
|
||||
src/service_path.o.uninstall:
|
||||
|
||||
src/usocket.o: src/usocket.c src/usocket.h src/utils.h src/fs.h
|
||||
@echo '[01;34m CC > [01;37msrc/usocket.o[00m'
|
||||
$(Q)$(CC) $(CFLAGS) -fPIC -std=c11 -c src/usocket.c -fPIC -std=c11 -o src/usocket.o
|
||||
|
@ -209,13 +233,13 @@ $(DESTDIR)$(INCLUDEDIR):
|
|||
$(DESTDIR)$(MANDIR):
|
||||
@echo '[01;35m DIR > [01;37m$(MANDIR)[00m'
|
||||
$(Q)mkdir -p $(DESTDIR)$(MANDIR)
|
||||
install: libipc.install man/libipc.7.install libipc.so.install libipc.a.install src/communication.o.install src/error.o.install src/fs.o.install src/message.o.install src/network.o.install src/print.o.install src/usocket.o.install src/utils.o.install src/communication.o.install src/error.o.install src/fs.o.install src/message.o.install src/network.o.install src/print.o.install src/usocket.o.install src/utils.o.install
|
||||
install: libipc.install man/libipc.7.install libipc.so.install libipc.a.install src/communication.o.install src/context.o.install src/error.o.install src/fs.o.install src/message.o.install src/network.o.install src/print.o.install src/service_path.o.install src/usocket.o.install src/utils.o.install src/communication.o.install src/context.o.install src/error.o.install src/fs.o.install src/message.o.install src/network.o.install src/print.o.install src/service_path.o.install src/usocket.o.install src/utils.o.install
|
||||
@:
|
||||
|
||||
uninstall: libipc.uninstall man/libipc.7.uninstall libipc.so.uninstall libipc.a.uninstall src/communication.o.uninstall src/error.o.uninstall src/fs.o.uninstall src/message.o.uninstall src/network.o.uninstall src/print.o.uninstall src/usocket.o.uninstall src/utils.o.uninstall src/communication.o.uninstall src/error.o.uninstall src/fs.o.uninstall src/message.o.uninstall src/network.o.uninstall src/print.o.uninstall src/usocket.o.uninstall src/utils.o.uninstall
|
||||
uninstall: libipc.uninstall man/libipc.7.uninstall libipc.so.uninstall libipc.a.uninstall src/communication.o.uninstall src/context.o.uninstall src/error.o.uninstall src/fs.o.uninstall src/message.o.uninstall src/network.o.uninstall src/print.o.uninstall src/service_path.o.uninstall src/usocket.o.uninstall src/utils.o.uninstall src/communication.o.uninstall src/context.o.uninstall src/error.o.uninstall src/fs.o.uninstall src/message.o.uninstall src/network.o.uninstall src/print.o.uninstall src/service_path.o.uninstall src/usocket.o.uninstall src/utils.o.uninstall
|
||||
@:
|
||||
|
||||
clean: libipc.clean man/libipc.7.clean libipc.so.clean libipc.a.clean src/communication.o.clean src/error.o.clean src/fs.o.clean src/message.o.clean src/network.o.clean src/print.o.clean src/usocket.o.clean src/utils.o.clean src/communication.o.clean src/error.o.clean src/fs.o.clean src/message.o.clean src/network.o.clean src/print.o.clean src/usocket.o.clean src/utils.o.clean
|
||||
clean: libipc.clean man/libipc.7.clean libipc.so.clean libipc.a.clean src/communication.o.clean src/context.o.clean src/error.o.clean src/fs.o.clean src/message.o.clean src/network.o.clean src/print.o.clean src/service_path.o.clean src/usocket.o.clean src/utils.o.clean src/communication.o.clean src/context.o.clean src/error.o.clean src/fs.o.clean src/message.o.clean src/network.o.clean src/print.o.clean src/service_path.o.clean src/usocket.o.clean src/utils.o.clean
|
||||
distclean: clean
|
||||
dist: dist-gz dist-xz dist-bz2
|
||||
$(Q)rm -- $(PACKAGE)-$(VERSION)
|
||||
|
@ -235,11 +259,13 @@ $(PACKAGE)-$(VERSION).tar.gz: distdir
|
|||
$(PACKAGE)-$(VERSION)/Makefile \
|
||||
$(PACKAGE)-$(VERSION)/project.zsh \
|
||||
$(PACKAGE)-$(VERSION)/src/communication.c \
|
||||
$(PACKAGE)-$(VERSION)/src/context.c \
|
||||
$(PACKAGE)-$(VERSION)/src/error.c \
|
||||
$(PACKAGE)-$(VERSION)/src/fs.c \
|
||||
$(PACKAGE)-$(VERSION)/src/message.c \
|
||||
$(PACKAGE)-$(VERSION)/src/network.c \
|
||||
$(PACKAGE)-$(VERSION)/src/print.c \
|
||||
$(PACKAGE)-$(VERSION)/src/service_path.c \
|
||||
$(PACKAGE)-$(VERSION)/src/usocket.c \
|
||||
$(PACKAGE)-$(VERSION)/src/utils.c \
|
||||
$(PACKAGE)-$(VERSION)/src/ipc.h \
|
||||
|
@ -259,11 +285,13 @@ $(PACKAGE)-$(VERSION).tar.xz: distdir
|
|||
$(PACKAGE)-$(VERSION)/Makefile \
|
||||
$(PACKAGE)-$(VERSION)/project.zsh \
|
||||
$(PACKAGE)-$(VERSION)/src/communication.c \
|
||||
$(PACKAGE)-$(VERSION)/src/context.c \
|
||||
$(PACKAGE)-$(VERSION)/src/error.c \
|
||||
$(PACKAGE)-$(VERSION)/src/fs.c \
|
||||
$(PACKAGE)-$(VERSION)/src/message.c \
|
||||
$(PACKAGE)-$(VERSION)/src/network.c \
|
||||
$(PACKAGE)-$(VERSION)/src/print.c \
|
||||
$(PACKAGE)-$(VERSION)/src/service_path.c \
|
||||
$(PACKAGE)-$(VERSION)/src/usocket.c \
|
||||
$(PACKAGE)-$(VERSION)/src/utils.c \
|
||||
$(PACKAGE)-$(VERSION)/src/ipc.h \
|
||||
|
@ -283,11 +311,13 @@ $(PACKAGE)-$(VERSION).tar.bz2: distdir
|
|||
$(PACKAGE)-$(VERSION)/Makefile \
|
||||
$(PACKAGE)-$(VERSION)/project.zsh \
|
||||
$(PACKAGE)-$(VERSION)/src/communication.c \
|
||||
$(PACKAGE)-$(VERSION)/src/context.c \
|
||||
$(PACKAGE)-$(VERSION)/src/error.c \
|
||||
$(PACKAGE)-$(VERSION)/src/fs.c \
|
||||
$(PACKAGE)-$(VERSION)/src/message.c \
|
||||
$(PACKAGE)-$(VERSION)/src/network.c \
|
||||
$(PACKAGE)-$(VERSION)/src/print.c \
|
||||
$(PACKAGE)-$(VERSION)/src/service_path.c \
|
||||
$(PACKAGE)-$(VERSION)/src/usocket.c \
|
||||
$(PACKAGE)-$(VERSION)/src/utils.c \
|
||||
$(PACKAGE)-$(VERSION)/src/ipc.h \
|
||||
|
@ -297,7 +327,7 @@ $(PACKAGE)-$(VERSION).tar.bz2: distdir
|
|||
$(PACKAGE)-$(VERSION)/src/utils.h
|
||||
|
||||
help:
|
||||
@echo '[01;37m :: libipc-0.6.0[00m'
|
||||
@echo '[01;37m :: libipc-0.7.2[00m'
|
||||
@echo ''
|
||||
@echo '[01;37mGeneric targets:[00m'
|
||||
@echo '[00m - [01;32mhelp [37m Prints this help message.[00m'
|
||||
|
|
35
README.md
35
README.md
|
@ -1,18 +1,43 @@
|
|||
|
||||
# OBSOLETED BY
|
||||
|
||||
This project was obsoleted by the [new Zig implementation][zigimpl].
|
||||
Code is smaller, simpler and safer to use.
|
||||
Packet format and API are a bit simpler, too.
|
||||
|
||||
# libipc
|
||||
|
||||
libipc - Simple, easy-to-use IPC library
|
||||
|
||||
See the introductory [man page](man/libipc.7.md).
|
||||
See the introductory man page in `man/libipc.7`.
|
||||
|
||||
See the presentation in [docs/libipc.md](docs/libipc.md).
|
||||
|
||||
# Compilation
|
||||
|
||||
`make`
|
||||
|
||||
# Since 0.7
|
||||
|
||||
# logging system
|
||||
- `libipc` have callbacks to use along with switching capabilities, making easier to implement proxies for different communication protocols
|
||||
|
||||
Logs are in one of the following directories: `$XDG_DATA_HOME/ipc/` or `$HOME/.local/share/ipc/`.
|
||||
The log file can be indicated with the `IPC_LOGFILE` environment variable, too.
|
||||
# Planning for 0.8
|
||||
|
||||
To remove logs: `make LDFLAGS=-DIPC_WITHOUT_ERRORS`
|
||||
For performance improvements within `libipc`:
|
||||
|
||||
- `libipc` will be rewritten in Zig -- **DONE!**
|
||||
- `libipc` shouldn't use realloc for each event (new client, new message, etc.) but by batch of a few thousand elements
|
||||
- `libipc` should use better internal structures, unrequiring the use of loops (over the whole list of messages or connections) for each action
|
||||
|
||||
# Planning for 0.9
|
||||
|
||||
- `libipc` should use `epoll/kqueue` for performance improvements
|
||||
* new functions will be added to the API
|
||||
* **but** we'll keep the same API for applications with no need for threading (way simpler implementation)
|
||||
- `libipc` should be thread-safe
|
||||
|
||||
# Planning for 1.0
|
||||
|
||||
- `libipc` should have usable bindings in several languages
|
||||
|
||||
[zigimpl]: https://git.baguette.netlib.re/Baguette/libipc
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
const std = @import("std");
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
|
||||
pub fn main() anyerror!void {
|
||||
const args = try std.process.argsAlloc(std.heap.page_allocator);
|
||||
|
||||
if (args.len <= 1) {
|
||||
try print_input();
|
||||
}
|
||||
|
||||
for (args[1..]) |f| {
|
||||
if (std.mem.eql(u8, f, "-")) { try print_input(); }
|
||||
else { try print_file (f); }
|
||||
}
|
||||
}
|
||||
|
||||
fn print_input() !void {
|
||||
try print_all (std.io.getStdIn());
|
||||
}
|
||||
|
||||
fn print_file(dest: []const u8) !void {
|
||||
var file = try std.fs.cwd().openFile(dest, .{ .mode = .read_only });
|
||||
defer file.close();
|
||||
try print_all (file);
|
||||
}
|
||||
|
||||
fn print_all(reader: std.fs.File) !void {
|
||||
var buffer: [4096]u8 = undefined;
|
||||
while (true) {
|
||||
const nbytes = try reader.read(&buffer);
|
||||
try stdout.print("{s}", .{buffer[0..nbytes]});
|
||||
if (nbytes == 0) break;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
catpoint
|
||||
pointtools
|
|
@ -0,0 +1,151 @@
|
|||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Extract layers from a FIG file
|
||||
#
|
||||
# History
|
||||
# 2002/04/23 : pda : design
|
||||
# 2013/02/12 : pda : extension to intervals
|
||||
#
|
||||
|
||||
usage ()
|
||||
{
|
||||
echo "usage: $0 layer[-layer] ... < fig-file > fig-file" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
if [ $# = 0 ]
|
||||
then
|
||||
usage
|
||||
fi
|
||||
|
||||
LAYERS="$*"
|
||||
|
||||
for i
|
||||
do
|
||||
if expr "$i" : "^[0-9][0-9]*-[0-9][0-9]*$" > /dev/null
|
||||
then
|
||||
MIN=`expr "$i" : "\([0-9]*\)-"`
|
||||
MAX=`expr "$i" : ".*-\([0-9]*\)"`
|
||||
if [ $MIN -gt $MAX ]
|
||||
then usage
|
||||
fi
|
||||
while [ $MIN -le $MAX ]
|
||||
do
|
||||
LAYERS="$LAYERS $MIN"
|
||||
MIN=`expr $MIN + 1`
|
||||
done
|
||||
elif expr "$i" : "^[0-9][0-9]*$" > /dev/null
|
||||
then
|
||||
LAYERS="$LAYERS $i"
|
||||
else
|
||||
usage
|
||||
fi
|
||||
done
|
||||
|
||||
awk -v "layers_string=$LAYERS" -F" " '
|
||||
BEGIN {
|
||||
split (layers_string, layers, "[ \t]")
|
||||
}
|
||||
/^#FIG/ {
|
||||
version = $2
|
||||
if (version != 3.2)
|
||||
{
|
||||
print "Invalid FIG version ($0)" > "/dev/stderr"
|
||||
}
|
||||
print
|
||||
afficher = 1
|
||||
next
|
||||
}
|
||||
/^0 / {
|
||||
# color pseudo object
|
||||
afficher = 1
|
||||
print
|
||||
next
|
||||
}
|
||||
/^1 / {
|
||||
# ellipse
|
||||
if (layerok($7))
|
||||
{
|
||||
afficher = 1
|
||||
print
|
||||
}
|
||||
else afficher = 0
|
||||
next
|
||||
}
|
||||
/^2 / {
|
||||
# polyline
|
||||
if (layerok($7))
|
||||
{
|
||||
afficher = 1
|
||||
print
|
||||
}
|
||||
else afficher = 0
|
||||
next
|
||||
}
|
||||
/^3 / {
|
||||
# spline
|
||||
if (layerok($7))
|
||||
{
|
||||
afficher = 1
|
||||
print
|
||||
}
|
||||
else afficher = 0
|
||||
next
|
||||
}
|
||||
/^4 / {
|
||||
# text
|
||||
if (layerok($4))
|
||||
{
|
||||
afficher = 1
|
||||
print
|
||||
}
|
||||
else afficher = 0
|
||||
next
|
||||
}
|
||||
/^5 / {
|
||||
# arc
|
||||
if (layerok($7))
|
||||
{
|
||||
afficher = 1
|
||||
print
|
||||
}
|
||||
else afficher = 0
|
||||
next
|
||||
}
|
||||
/^6 / {
|
||||
# compound
|
||||
afficher = 1
|
||||
print
|
||||
next
|
||||
}
|
||||
/^-6 / {
|
||||
# end of compound
|
||||
afficher = 1
|
||||
print
|
||||
next
|
||||
}
|
||||
/^ / {
|
||||
# ligne de continuation
|
||||
if (afficher)
|
||||
{
|
||||
print
|
||||
}
|
||||
next
|
||||
}
|
||||
{
|
||||
afficher = 1
|
||||
print
|
||||
next
|
||||
}
|
||||
|
||||
function layerok (l, ok, n)
|
||||
{
|
||||
ok = 0
|
||||
for (n in layers)
|
||||
{
|
||||
if (l == layers [n])
|
||||
ok = 1
|
||||
}
|
||||
return ok
|
||||
}' -
|
|
@ -0,0 +1,34 @@
|
|||
#!/bin/bash
|
||||
|
||||
for i in *.fig
|
||||
do
|
||||
# latin1 accents
|
||||
grep -E "[ôéèàîÉê]" $i 2>/dev/null 1>/dev/null
|
||||
if [ $? -eq 0 ]; then
|
||||
echo $i matches a latin1 accent
|
||||
sed -r -i "s/É/\\\311/g" $i
|
||||
sed -r -i "s/à/\\\340/g" $i
|
||||
sed -r -i "s/è/\\\350/g" $i
|
||||
sed -r -i "s/é/\\\351/g" $i
|
||||
sed -r -i "s/ê/\\\352/g" $i
|
||||
sed -r -i "s/î/\\\356/g" $i
|
||||
sed -r -i "s/ô/\\\364/g" $i
|
||||
# sed -r -i "s/°/\\\176/g" $i
|
||||
fi
|
||||
|
||||
PDF=$(echo ${i} | sed "s/fig$/pdf/")
|
||||
|
||||
if [ ! -f ${PDF} ] || [ $(stat -c "%X" ${PDF}) -lt $(stat -c "%X" ${i}) ]
|
||||
then
|
||||
|
||||
echo "fig2ps ${i}"
|
||||
fig2dev -L pdf ${i} > ${PDF}
|
||||
|
||||
echo "touch ${PDF}"
|
||||
touch ${PDF}
|
||||
|
||||
# echo "make and touch ${PDF}"
|
||||
# pdf2ps ${PDF}
|
||||
# touch ${PDF}
|
||||
fi
|
||||
done
|
|
@ -0,0 +1,109 @@
|
|||
#FIG 3.2 Produced by xfig version 3.2.7a
|
||||
Landscape
|
||||
Center
|
||||
Metric
|
||||
A4
|
||||
100.00
|
||||
Single
|
||||
-2
|
||||
1200 2
|
||||
5 1 0 3 0 7 30 -1 -1 0.000 0 0 1 0 3646.767 3431.191 2160 4950 1530 3240 2340 1755
|
||||
1 1 2.00 90.00 150.00
|
||||
2 2 0 1 0 7 30 -1 -1 0.000 0 0 -1 0 0 5
|
||||
8955 1395 11115 1395 11115 1755 8955 1755 8955 1395
|
||||
2 1 0 3 0 7 30 -1 -1 0.000 0 0 -1 1 1 2
|
||||
1 1 2.00 75.00 150.00
|
||||
1 1 2.00 75.00 150.00
|
||||
4230 1530 8955 1530
|
||||
2 1 0 3 0 7 30 -1 -1 0.000 0 0 -1 1 1 2
|
||||
1 1 2.00 75.00 150.00
|
||||
1 1 2.00 75.00 150.00
|
||||
4230 1665 8955 3150
|
||||
2 1 0 3 0 7 15 -1 -1 0.000 0 0 -1 1 1 2
|
||||
1 1 2.00 75.00 150.00
|
||||
1 1 2.00 75.00 150.00
|
||||
9990 3420 9990 4950
|
||||
2 2 0 1 0 7 5 -1 -1 0.000 0 0 -1 0 0 5
|
||||
8955 4950 11115 4950 11115 5310 8955 5310 8955 4950
|
||||
2 2 0 1 0 7 15 -1 -1 0.000 0 0 -1 0 0 5
|
||||
8955 3060 11115 3060 11115 3420 8955 3420 8955 3060
|
||||
2 2 0 1 0 7 5 -1 -1 0.000 0 0 -1 0 0 5
|
||||
2070 4950 4230 4950 4230 5310 2070 5310 2070 4950
|
||||
2 1 0 3 0 7 10 -1 -1 0.000 0 0 -1 1 1 2
|
||||
1 1 3.00 90.00 150.00
|
||||
1 1 3.00 90.00 150.00
|
||||
4230 5130 8955 5130
|
||||
2 2 0 1 0 7 15 -1 -1 0.000 0 0 -1 0 0 5
|
||||
2070 3060 2880 3060 2880 3420 2070 3420 2070 3060
|
||||
2 2 0 1 0 7 15 -1 -1 0.000 0 0 -1 0 0 5
|
||||
3375 3060 4185 3060 4185 3420 3375 3420 3375 3060
|
||||
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 1
|
||||
6435 2295
|
||||
2 2 0 1 0 7 30 -1 -1 0.000 0 0 -1 0 0 5
|
||||
2070 1395 4230 1395 4230 1755 2070 1755 2070 1395
|
||||
2 1 0 3 0 7 25 -1 -1 0.000 0 0 -1 1 1 2
|
||||
1 1 3.00 90.00 150.00
|
||||
1 1 3.00 90.00 150.00
|
||||
9360 6345 10620 6345
|
||||
2 1 1 3 1 7 25 -1 -1 8.000 0 0 -1 1 1 2
|
||||
1 1 3.00 90.00 150.00
|
||||
1 1 3.00 90.00 150.00
|
||||
9225 4950 9225 3420
|
||||
2 1 1 3 1 7 25 -1 -1 8.000 0 0 -1 1 1 2
|
||||
1 1 3.00 90.00 150.00
|
||||
1 1 3.00 90.00 150.00
|
||||
4185 3105 8955 3105
|
||||
2 1 1 3 1 7 25 -1 -1 2.000 0 0 -1 1 1 2
|
||||
1 1 1.00 60.00 90.00
|
||||
1 1 1.00 60.00 90.00
|
||||
2880 3105 3375 3105
|
||||
2 1 1 3 1 7 25 -1 -1 8.000 0 0 -1 1 1 2
|
||||
1 1 3.00 90.00 150.00
|
||||
1 1 3.00 90.00 150.00
|
||||
2430 4950 2430 3420
|
||||
2 1 1 3 1 7 25 -1 -1 8.000 0 0 -1 1 1 2
|
||||
1 1 3.00 90.00 150.00
|
||||
1 1 3.00 90.00 150.00
|
||||
9360 6075 10620 6075
|
||||
2 1 0 3 0 7 25 -1 -1 0.000 0 0 -1 1 1 2
|
||||
1 1 3.00 90.00 150.00
|
||||
1 1 3.00 90.00 150.00
|
||||
3780 4950 3780 3420
|
||||
2 1 0 3 0 7 25 -1 -1 0.000 0 0 -1 1 1 2
|
||||
1 1 3.00 90.00 150.00
|
||||
1 1 3.00 90.00 150.00
|
||||
4185 3375 8955 3375
|
||||
2 2 0 0 0 7 999 -1 -1 0.000 0 0 -1 0 0 5
|
||||
0 0 13365 0 13365 7830 0 7830 0 0
|
||||
4 1 0 30 -1 16 20 0.0000 4 240 5625 6705 540 LibIPC communications with a browser\001
|
||||
4 1 0 30 -1 16 16 0.0000 4 210 1620 3105 1665 Web Browser\001
|
||||
4 1 0 30 -1 16 16 0.0000 4 210 1545 3105 5220 JS or WASM\001
|
||||
4 1 0 30 -1 16 16 0.0000 4 210 1425 9990 1665 Web Server\001
|
||||
4 1 0 30 -1 16 16 5.9865 4 270 2460 6615 2295 dynamic interactions\001
|
||||
4 1 0 30 -1 16 14 5.9865 4 225 3465 6435 2700 (the webserver could be a proxy)\001
|
||||
4 1 0 30 -1 16 16 0.0000 4 210 1485 9990 3330 Websocketd\001
|
||||
4 1 0 30 -1 16 16 0.0000 4 210 1185 6705 1395 static files\001
|
||||
4 1 0 5 -1 16 16 0.0000 4 210 795 9990 5220 Server\001
|
||||
4 1 0 20 -1 16 20 0.0000 4 240 4560 6705 540 Remote libIPC communications\001
|
||||
4 1 0 10 -1 16 20 0.0000 4 240 4200 6705 540 Local libIPC communications\001
|
||||
4 0 0 15 -1 16 16 0.0000 4 210 2475 10170 4275 IPC communications\001
|
||||
4 0 0 50 -1 16 12 0.0000 4 150 855 2115 5895 10 = local\001
|
||||
4 0 0 50 -1 16 12 0.0000 4 150 1305 2115 6135 15 = all remote\001
|
||||
4 0 0 50 -1 16 12 0.0000 4 135 1050 2115 6375 20 = remote\001
|
||||
4 0 0 50 -1 16 12 0.0000 4 150 795 2115 6615 30 = web\001
|
||||
4 0 0 50 -1 16 12 0.0000 4 150 645 2115 5670 05 = all\001
|
||||
4 1 0 10 -1 16 16 0.0000 4 255 1710 6570 5490 (Unix sockets)\001
|
||||
4 1 0 10 -1 16 16 0.0000 4 210 2595 6570 4950 Direct communication\001
|
||||
4 1 0 11 -1 16 16 0.0000 4 210 690 3060 5220 Client\001
|
||||
4 0 0 50 -1 16 12 0.0000 4 150 1890 3375 5895 12 = local and remote\001
|
||||
4 1 0 20 -1 16 16 0.0000 4 210 690 3780 3330 TCPd\001
|
||||
4 1 0 20 -1 16 16 0.0000 4 210 600 2475 3330 IPCd\001
|
||||
4 0 0 50 -1 16 12 0.0000 4 195 2040 3375 6375 21 = IPCd explanations\001
|
||||
4 1 0 20 -1 16 16 0.0000 4 210 690 9990 3330 TCPd\001
|
||||
4 2 0 25 -1 16 10 0.0000 4 150 1395 2205 4140 tcp://remote/server\001
|
||||
4 2 0 25 -1 16 10 0.0000 4 120 675 9090 6390 Data flow\001
|
||||
4 2 0 25 -1 16 10 0.0000 4 120 1875 9090 6120 Connection establishment\001
|
||||
4 0 0 21 -1 16 14 0.0000 4 225 3825 4455 1620 contacts the right "protocol deamon"\001
|
||||
4 0 0 21 -1 16 14 0.0000 4 225 6690 4455 1935 after connection establishment, provides the socket to the client\001
|
||||
4 0 0 21 -1 16 14 0.0000 4 225 3600 4455 2250 therefore, never handles data flow\001
|
||||
4 2 18 21 -1 18 14 0.0000 4 180 630 4275 1620 IPCd:\001
|
|
@ -0,0 +1,380 @@
|
|||
## Before starting
|
||||
|
||||
This file is a presentation based on the point tools:
|
||||
https://git.baguette.netlib.re/Baguette/pointtools
|
||||
|
||||
To see it, type 'make'.
|
||||
|
||||
TODO: Explain the problem
|
||||
TODO: Explain the solution
|
||||
TODO: Explain why LibIPC
|
||||
TODO: Explain how LibIPC
|
||||
TODO: Explain other possible implementations
|
||||
TODO: Explain future of LibIPC
|
||||
TODO: Explain what can be done right now
|
||||
TODO: Explain what I actually do with it
|
||||
TODO: Explain LibIPC isn't a silver bullet
|
||||
|
||||
Have fun!
|
||||
|
||||
## Programming problems
|
||||
|
||||
Libraries
|
||||
* change often = hard to follow
|
||||
* don't often provide a high-level interface
|
||||
* each library is coded in its own way
|
||||
* availability vary depending on the language
|
||||
|
||||
Example: libraries to access databases
|
||||
* languages often have their own implementation
|
||||
* functions may vary from a language to another
|
||||
|
||||
## Infrastructure problems
|
||||
|
||||
Infrastructure
|
||||
* Always need to install all required libraries
|
||||
* No clear way to sandbox part of an application
|
||||
|
||||
## What solution?
|
||||
|
||||
MAKE APPLICATIONS, NOT LIBRARIES
|
||||
* apps talking to apps
|
||||
|
||||
Create an abstraction for libraries
|
||||
* languages, implementations, code modifications in general
|
||||
|
||||
Create an abstraction for network code
|
||||
* applications think communications are local
|
||||
* networking is performed by dedicated services
|
||||
* examples: TCPd, UDPd, TLSd, HTTPd...
|
||||
* apps are independant from protocols and formats
|
||||
(unless they are fundamentaly network-related)
|
||||
|
||||
## In practice
|
||||
|
||||
usage must be simple
|
||||
|
||||
1. init connection or service
|
||||
2. loop over events
|
||||
|
||||
#pause
|
||||
events are simple and high level
|
||||
|
||||
1. connection and disconnection
|
||||
2. message received and sent
|
||||
|
||||
#pause
|
||||
message have a simple format: length + value
|
||||
|
||||
#pause
|
||||
And that's it.
|
||||
## When
|
||||
|
||||
LibIPC is useful when the app:
|
||||
|
||||
- cannot be a simple shell command
|
||||
- needs a bidirectional communication
|
||||
- is an abstraction over a library
|
||||
|
||||
## Available libraries
|
||||
|
||||
* DBUS
|
||||
* libevent
|
||||
* even more complicated stuff
|
||||
* RPC-style, like Corba
|
||||
|
||||
#pause
|
||||
* ... or bare libc api
|
||||
* shared memory
|
||||
* pipes
|
||||
* sockets (unix, inet, inet6)
|
||||
|
||||
## DBUS
|
||||
|
||||
* not well suited for our needs
|
||||
(a polite way to say: what a bloody mess)
|
||||
|
||||
Is it designed *NOT* to be used?
|
||||
* over-engineered
|
||||
* complex
|
||||
* documentation isn't great
|
||||
* no code example
|
||||
|
||||
## DBUS (bonus page!)
|
||||
|
||||
DBUS feels obsolete: a big chunk of the documentation is
|
||||
about message format. Just use CBOR already!
|
||||
|
||||
#pause
|
||||
They even admit they did a poor job on the C part:
|
||||
> There is a low-level C binding, but that is probably too detailed
|
||||
> and cumbersome for anything but writing other bindings.
|
||||
|
||||
#pause
|
||||
Oh. And C++. YOU SHALL NOT PASS!
|
||||
|
||||
This is a Linux requirement nowadays, wth?
|
||||
|
||||
## libevent
|
||||
|
||||
* works with epoll and kqueue
|
||||
* great performances
|
||||
* works on Linux and *BSD
|
||||
|
||||
* a bit complicated
|
||||
|
||||
## Bare libc api
|
||||
|
||||
shared memory and semaphores
|
||||
|
||||
* (kinda) complicated api
|
||||
* not about exchanging messages
|
||||
|
||||
|
||||
pipes, sockets
|
||||
|
||||
* lack a conventional message format
|
||||
... but that's about it
|
||||
* Great to start with!
|
||||
|
||||
All have great performances to exchange data.
|
||||
|
||||
What is slow is the function to _wait_ for new events.
|
||||
|
||||
## LibIPC's choice
|
||||
|
||||
Unix sockets
|
||||
- fast, simple, reliable, bidirectional
|
||||
- remote connections will have their own service (ex: TCPd)
|
||||
|
||||
Dumbest possible message format
|
||||
- length + value
|
||||
- build your own format on top of it!
|
||||
|
||||
Wait on file descriptors with poll(2)
|
||||
- slow, but available everywhere
|
||||
- may upgrade to libevent
|
||||
|
||||
## LibIPC history (1/3)
|
||||
|
||||
1. based on pipes
|
||||
* because we gotta go fast!
|
||||
* ... but implementation was a bit of a mess
|
||||
|
||||
#pause
|
||||
2. rewrite to work with unix sockets
|
||||
* performances are excellent, no need for _absolute best_
|
||||
* way nicer implementation
|
||||
* select(2) for listening on file descriptors
|
||||
#pause
|
||||
* ... wait, does select(2) support more than 1024 connections?
|
||||
|
||||
## LibIPC history (2/3)
|
||||
|
||||
3. rewrite using poll(2)
|
||||
* many bugfixes later, way more tested than before
|
||||
* implementation was (kinda) production-ready
|
||||
* implementation was simple: < 2000 lines of C code
|
||||
|
||||
Still wasn't as simple as I wanted
|
||||
|
||||
## LibIPC history (3/3)
|
||||
|
||||
4. rewrite in Zig
|
||||
* still uses poll(2) (at least for now)
|
||||
* C-compatible bindings are available
|
||||
|
||||
## Why Zig? (1/2)
|
||||
|
||||
error management is built-in and mandatory
|
||||
|
||||
simpler to read and write
|
||||
* nicer data structures (contain functions)
|
||||
* less code redundancy (defer, more generic functions)
|
||||
* no more C's pitfalls
|
||||
* fully qualified names
|
||||
|
||||
## Why Zig? (2/2)
|
||||
|
||||
better standard library
|
||||
* usual structures: lists, hashtables
|
||||
* log system
|
||||
|
||||
memory management is simpler, more secure and more flexible
|
||||
|
||||
better at exposing bugs (better type system)
|
||||
|
||||
simpler to cross-compile: same standard library for all OSs
|
||||
|
||||
## Current implementation of libIPC
|
||||
|
||||
bindings available in Crystal
|
||||
* as well as fancy mappings: JSON and CBOR class serialization
|
||||
|
||||
#pause
|
||||
epoll (Linux) and kqueue (*BSD) were avoided
|
||||
* because callbacks hell => harder to read and to write code
|
||||
#pause
|
||||
* still a possibility for someday, not the priority right now
|
||||
|
||||
#pause
|
||||
LibIPC doesn't handle parallelism, yet
|
||||
|
||||
## How libIPC works (in Zig)
|
||||
|
||||
LibIPC has a high level API
|
||||
|
||||
var context = try Context.init(allocator);
|
||||
defer context.deinit();
|
||||
|
||||
#pause
|
||||
|
||||
var pong_fd = try context.connect_service ("pong");
|
||||
var message = try Message.init (pong_fd, allocator, "hello");
|
||||
try context.schedule (message);
|
||||
|
||||
## How libIPC works (in Zig)
|
||||
|
||||
var event = try context.wait_event();
|
||||
|
||||
switch (event.t) {
|
||||
...
|
||||
}
|
||||
|
||||
## How libIPC works (in Zig)
|
||||
|
||||
var event = try context.wait_event();
|
||||
|
||||
switch (event.t) {
|
||||
|
||||
.CONNECTION => {
|
||||
print ("New client!\n", .{});
|
||||
},
|
||||
|
||||
...
|
||||
}
|
||||
|
||||
|
||||
## How libIPC works (in Zig)
|
||||
|
||||
var event = try context.wait_event();
|
||||
|
||||
switch (event.t) {
|
||||
|
||||
.CONNECTION => {
|
||||
print ("New client!\n", .{});
|
||||
},
|
||||
|
||||
.MESSAGE_RX => {
|
||||
if (event.m) |m| {
|
||||
print ("a message has been received: {s}\n", .{m});
|
||||
}
|
||||
}
|
||||
...
|
||||
}
|
||||
|
||||
## How libIPC works (bindings)
|
||||
|
||||
1. init a connection (client) or create an unix socket (service)
|
||||
|
||||
ipc_connect_service (context, &fd, service_name, service_len)
|
||||
ipc_service_init (context, &fd, service_name, service_len)
|
||||
|
||||
#pause
|
||||
2. loop, wait for events
|
||||
listening to file descriptors (libIPC ones or not)
|
||||
|
||||
example:
|
||||
|
||||
while(1) {
|
||||
ipc_wait_event (context, &type, &index, &fd, buffer, &buffer_len)
|
||||
switch (type) {
|
||||
case IPC_CONNECTION : ...
|
||||
case IPC_DISCONNECTION : ...
|
||||
case IPC_MESSAGE: ...
|
||||
}
|
||||
}
|
||||
|
||||
## How libIPC works
|
||||
|
||||
3. send messages
|
||||
|
||||
```c
|
||||
ipc_schedule (context, fd, buffer, buffer_len)
|
||||
or
|
||||
ipc_write (context, fd, buffer, buffer_len)
|
||||
```
|
||||
|
||||
#pause
|
||||
4. add a file descriptor to listen to
|
||||
|
||||
ipc_add_external (context, fd)
|
||||
|
||||
## How libIPC works
|
||||
|
||||
LibIPC also helps to create "protocol daemons" like TCPd with
|
||||
automatic switching between file descriptors
|
||||
|
||||
LibIPC takes callbacks to obtain libipc payloads inside arbitrary message structure
|
||||
|
||||
Example: websocketd.
|
||||
Clients exchange data with a libipc service through websockets messages.
|
||||
|
||||
websocketd binds both the client and its service file descriptors,
|
||||
then provides the libipc a callback to extract libipc messages from
|
||||
the websocket messages sent by the client.
|
||||
|
||||
Same thing the other way.
|
||||
|
||||
|
||||
ipc_switching_callbacks (context, client_fd, cb_in, cb_out)
|
||||
|
||||
## libIPC internal structures (1/2)
|
||||
|
||||
Main goal: simplest possible structures
|
||||
|
||||
Examples (nothing hidden):
|
||||
|
||||
Message {
|
||||
fd: i32 => File descriptor concerned about this message.
|
||||
payload: []u8 => Actual payload.
|
||||
allocator: std.mem.Allocator => Memory management.
|
||||
};
|
||||
|
||||
Event {
|
||||
t: Event.Type => Example: connection, message tx, ...
|
||||
m: ?Message => Message, if there is one.
|
||||
index: usize => (Internal stuff).
|
||||
originfd: i32 => File descriptor related to the event.
|
||||
};
|
||||
|
||||
## libIPC internal structures (2/2)
|
||||
|
||||
Context structure is slightly more complicated, but _reasonable_.
|
||||
|
||||
Context {
|
||||
rundir: [] u8, // Where the UNIX sockets are.
|
||||
pollfd: PollFD, // File descriptors to manage.
|
||||
tx: Messages, // Messages to send, once their fd is available.
|
||||
...
|
||||
};
|
||||
|
||||
The rest is implementation details (and more advanced usage of LibIPC).
|
||||
|
||||
## Future of libIPC
|
||||
|
||||
## Why not use it?
|
||||
|
||||
Current limitations
|
||||
|
||||
* performances (libIPC is based on poll(2), not epoll nor kqueue)
|
||||
* it really isn't an issue until you have hundreds of clients
|
||||
* LibIPC could someday use libevent
|
||||
|
||||
* nothing in libIPC is thread-safe
|
||||
|
||||
These limitations are the price for a simple implementation.
|
||||
|
||||
## Questions?
|
||||
|
||||
Ask! `karchnu at karchnu.fr`
|
|
@ -0,0 +1,30 @@
|
|||
all: get-point-tools clean generate display
|
||||
|
||||
BAGUETTE=https://git.baguette.netlib.re/Baguette
|
||||
get-point-tools:
|
||||
@test -d catpoint || (git clone $(BAGUETTE)/catpoint.git && cd catpoint && make)
|
||||
@test -d pointtools || (git clone $(BAGUETTE)/pointtools.git && cd pointtools && make)
|
||||
|
||||
CATPOINT = $(PWD)/catpoint/catpoint
|
||||
MD2POINT = $(PWD)/pointtools/bin/md2point
|
||||
PRESENTATION = $(PWD)/libipc.md
|
||||
|
||||
BDIR=build
|
||||
clean:
|
||||
@-rm -r $(BDIR) 2>/dev/null || true
|
||||
|
||||
generate:
|
||||
@test -d $(BDIR) || mkdir $(BDIR)
|
||||
@cd $(BDIR) && (cat $(PRESENTATION) | $(MD2POINT))
|
||||
|
||||
display:
|
||||
@cd $(BDIR) && $(CATPOINT) *.txt
|
||||
|
||||
help:
|
||||
@echo "get-point-tools: get all relevant point tools (catpoint and md2point)"
|
||||
@echo
|
||||
@echo "generate: convert markdown into 'point' documents"
|
||||
@echo "display: run catpoint on all 'point' document"
|
||||
@echo "clean: remove all 'point' files"
|
||||
@echo
|
||||
@echo "By default: get point tools, generate then display the presentation"
|
|
@ -0,0 +1,21 @@
|
|||
all: allfigures
|
||||
|
||||
FIGS=$(shell ls figs/*.fig)
|
||||
FIGL=./figlayers
|
||||
|
||||
LIBIPC=figs/libipc
|
||||
|
||||
figlibipc: $(LIBIPC).fig
|
||||
echo "libipc"
|
||||
$(FIGL) 999 5 10-12 < $(LIBIPC).fig > $(LIBIPC)-1.fig # local
|
||||
$(FIGL) 999 5 11-12 15 20-25 < $(LIBIPC).fig > $(LIBIPC)-2.fig # remote
|
||||
$(FIGL) 999 5 15 30 < $(LIBIPC).fig > $(LIBIPC)-3.fig # web remote
|
||||
|
||||
allfigures: figlibipc
|
||||
echo "make ps"
|
||||
cd figs/ ; ./graph-this.sh
|
||||
# cd diag/ ; ./graph-this.sh
|
||||
|
||||
clean:
|
||||
echo "rm figs/*.ps"
|
||||
rm figs/*.ps
|
|
@ -24,7 +24,7 @@ int main (int argc, char *argv[])
|
|||
|
||||
T_PERROR_R (((fd = open (argv[1], O_CREAT | O_RDWR)) < 0), "cannot open the file", EXIT_FAILURE);
|
||||
|
||||
TEST_IPC_Q (usock_connect (&sock, "SOCKET_FD_EXCHANGE_TEST"), EXIT_FAILURE);
|
||||
TEST_IPC_Q (usock_connect (&sock, "./SOCKET_FD_EXCHANGE_TEST"), EXIT_FAILURE);
|
||||
TEST_IPC_Q (ipc_provide_fd (sock, fd), EXIT_FAILURE);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
|
|
|
@ -24,7 +24,7 @@ int main (int argc, char *argv[])
|
|||
int usockclient = 0;
|
||||
int fd = 0;
|
||||
|
||||
TEST_IPC_Q (usock_init (&usock, "SOCKET_FD_EXCHANGE_TEST"), EXIT_FAILURE);
|
||||
TEST_IPC_Q (usock_init (&usock, "./SOCKET_FD_EXCHANGE_TEST"), EXIT_FAILURE);
|
||||
TEST_IPC_Q (usock_accept (usock, &usockclient), EXIT_FAILURE);
|
||||
TEST_IPC_Q (ipc_receive_fd (usockclient, &fd), EXIT_FAILURE);
|
||||
|
||||
|
|
|
@ -23,14 +23,14 @@ void chomp (char *str, ssize_t len)
|
|||
}
|
||||
}
|
||||
|
||||
struct ipc_connection_info *srv;
|
||||
struct ipc_ctx *ctx = NULL;
|
||||
|
||||
void non_interactive (int verbosity, size_t nb_msg, char *msg_str, char *env[])
|
||||
void non_interactive (int verbosity, size_t nb_msg, char *msg_str)
|
||||
{
|
||||
SECURE_DECLARATION (struct ipc_message, m);
|
||||
|
||||
// init service
|
||||
TEST_IPC_QUIT_ON_ERROR (ipc_connection (env, srv, SERVICE_NAME), EXIT_FAILURE);
|
||||
TEST_IPC_QUIT_ON_ERROR (ipc_connection (ctx, SERVICE_NAME, NULL), EXIT_FAILURE);
|
||||
|
||||
if (verbosity > 1) {
|
||||
printf ("msg to send (%ld): %.*s\n", (ssize_t) strlen (MSG) + 1, (int)strlen (MSG), MSG);
|
||||
|
@ -38,9 +38,12 @@ void non_interactive (int verbosity, size_t nb_msg, char *msg_str, char *env[])
|
|||
|
||||
for (size_t i = 0 ; i < nb_msg ; i++) {
|
||||
TEST_IPC_QUIT_ON_ERROR (ipc_message_format_data (&m, 42, msg_str, (ssize_t) strlen (msg_str) + 1), EXIT_FAILURE);
|
||||
TEST_IPC_QUIT_ON_ERROR (ipc_write (srv, &m), EXIT_FAILURE);
|
||||
TEST_IPC_QUIT_ON_ERROR (ipc_write_fd (ctx->pollfd[0].fd, &m), EXIT_FAILURE);
|
||||
if (verbosity > 1) {
|
||||
printf ("msg written (type: %u): %s\n", m.user_type, m.payload);
|
||||
}
|
||||
ipc_message_empty (&m);
|
||||
TEST_IPC_QUIT_ON_ERROR (ipc_read (srv, &m), EXIT_FAILURE);
|
||||
TEST_IPC_QUIT_ON_ERROR (ipc_read (ctx, 0 /* read from the only valid index */, &m), EXIT_FAILURE);
|
||||
|
||||
if (verbosity > 1) {
|
||||
printf ("msg recv (type: %u): %s\n", m.user_type, m.payload);
|
||||
|
@ -48,30 +51,28 @@ void non_interactive (int verbosity, size_t nb_msg, char *msg_str, char *env[])
|
|||
ipc_message_empty (&m);
|
||||
}
|
||||
|
||||
TEST_IPC_QUIT_ON_ERROR (ipc_close (srv), EXIT_FAILURE);
|
||||
TEST_IPC_QUIT_ON_ERROR (ipc_close_all (ctx), EXIT_FAILURE);
|
||||
}
|
||||
|
||||
void interactive (char *env[])
|
||||
void interactive ()
|
||||
{
|
||||
// init service
|
||||
TEST_IPC_QUIT_ON_ERROR (ipc_connection (env, srv, SERVICE_NAME), EXIT_FAILURE);
|
||||
TEST_IPC_QUIT_ON_ERROR (ipc_connection (ctx, SERVICE_NAME, NULL), EXIT_FAILURE);
|
||||
|
||||
SECURE_DECLARATION (struct ipc_error, ret);
|
||||
SECURE_DECLARATION (struct ipc_event, event);
|
||||
SECURE_DECLARATION (struct ipc_connection_infos, services);
|
||||
|
||||
ipc_add (&services, srv);
|
||||
ipc_add_fd (&services, 0); // add STDIN
|
||||
ipc_add_fd (ctx, 0); // add STDIN
|
||||
|
||||
ipc_connections_print (&services);
|
||||
ipc_ctx_print (ctx);
|
||||
|
||||
long timer = 10;
|
||||
int timer = 10000;
|
||||
|
||||
while (1) {
|
||||
printf ("msg to send: ");
|
||||
fflush (stdout);
|
||||
|
||||
TEST_IPC_WAIT_EVENT_Q (ipc_wait_event (&services, NULL, &event, &timer), EXIT_FAILURE);
|
||||
TEST_IPC_WAIT_EVENT_Q (ipc_wait_event (ctx, &event, &timer), EXIT_FAILURE);
|
||||
switch (event.type) {
|
||||
case IPC_EVENT_TYPE_EXTRA_SOCKET:
|
||||
{
|
||||
|
@ -80,7 +81,7 @@ void interactive (char *env[])
|
|||
char buf[4096];
|
||||
memset (buf, 0, 4096);
|
||||
|
||||
len = read (event.origin->fd, buf, 4096);
|
||||
len = read (event.origin, buf, 4096);
|
||||
|
||||
buf[len - 1] = '\0';
|
||||
chomp (buf, len);
|
||||
|
@ -93,13 +94,13 @@ void interactive (char *env[])
|
|||
// in case we want to quit the program
|
||||
if (len == 0 || strncmp (buf, "quit", 4) == 0 || strncmp (buf, "exit", 4) == 0) {
|
||||
|
||||
struct ipc_error ret = ipc_close (srv);
|
||||
struct ipc_error ret = ipc_close_all (ctx);
|
||||
if (ret.error_code != IPC_ERROR_NONE) {
|
||||
fprintf (stderr, "%s", ret.error_message);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
ipc_connections_free (&services);
|
||||
ipc_ctx_free (ctx);
|
||||
|
||||
exit (EXIT_SUCCESS);
|
||||
}
|
||||
|
@ -112,16 +113,16 @@ void interactive (char *env[])
|
|||
fprintf (stderr, "%s", ret.error_message);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
#if 0
|
||||
#if 1
|
||||
printf ("\n");
|
||||
printf ("right before sending a message\n");
|
||||
#endif
|
||||
ret = ipc_write (srv, m);
|
||||
ret = ipc_write_fd (ctx->pollfd[1].fd, m);
|
||||
if (ret.error_code != IPC_ERROR_NONE) {
|
||||
fprintf (stderr, "%s", ret.error_message);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
#if 0
|
||||
#if 1
|
||||
printf ("right after sending a message\n");
|
||||
#endif
|
||||
|
||||
|
@ -145,24 +146,17 @@ void interactive (char *env[])
|
|||
}
|
||||
}
|
||||
|
||||
int main (int argc, char *argv[], char *env[])
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
printf("usage: %s [verbosity #messages message]", argv[0]);
|
||||
printf("usage: %s [verbosity #messages message]\n", argv[0]);
|
||||
|
||||
// $0:
|
||||
argv = argv; // warnings
|
||||
|
||||
srv = malloc (sizeof (struct ipc_connection_info));
|
||||
memset (srv, 0, sizeof (struct ipc_connection_info));
|
||||
|
||||
// index and version should be filled
|
||||
srv->index = 0;
|
||||
srv->version = 0;
|
||||
ctx = malloc (sizeof (struct ipc_ctx));
|
||||
memset (ctx, 0, sizeof (struct ipc_ctx));
|
||||
|
||||
if (argc == 4)
|
||||
non_interactive (atoi(argv[1]), (size_t) atoi(argv[2]), argv[3], env);
|
||||
non_interactive (atoi(argv[1]), (size_t) atoi(argv[2]), argv[3]);
|
||||
else
|
||||
interactive (env);
|
||||
interactive ();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
101
examples/pongd.c
101
examples/pongd.c
|
@ -12,36 +12,47 @@
|
|||
fprintf(stderr, "error while %s: %s\n", msg, ret.error_message);\
|
||||
}
|
||||
|
||||
/**
|
||||
* ipc_ctx:
|
||||
* cinfos: array of ipc_connection_info
|
||||
* pollfd: array of `pollfd` structure
|
||||
* Both arrays share the same indices.
|
||||
*/
|
||||
|
||||
/**
|
||||
******************************************************************************
|
||||
* Overview of the main loop:
|
||||
* 1. "ctx" pointer declaration (struct ipc_ctx).
|
||||
* 1. ipc_server_init (ctx, SERVICE_NAME)
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
int cpt = 0;
|
||||
|
||||
int verbosity = 1;
|
||||
|
||||
struct ipc_connection_info *srv = NULL;
|
||||
struct ipc_connection_infos *clients = NULL;
|
||||
struct ipc_ctx *ctx = NULL;
|
||||
|
||||
void main_loop ()
|
||||
{
|
||||
double base_timer = 0;
|
||||
double timer = base_timer;
|
||||
int base_timer = 10000;
|
||||
int timer = base_timer;
|
||||
SECURE_DECLARATION (struct ipc_error, ret);
|
||||
|
||||
clients = malloc (sizeof (struct ipc_connection_infos));
|
||||
memset (clients, 0, sizeof (struct ipc_connection_infos));
|
||||
|
||||
SECURE_DECLARATION (struct ipc_event, event);
|
||||
event.type = IPC_EVENT_TYPE_NOT_SET;
|
||||
|
||||
while (1) {
|
||||
// ipc_service_poll_event provides one event at a time
|
||||
// ipc_wait_event provides one event at a time
|
||||
// warning: event->m is free'ed if not NULL
|
||||
TEST_IPC_WAIT_EVENT_Q (ipc_wait_event (clients, srv, &event, &timer), EXIT_FAILURE);
|
||||
TEST_IPC_WAIT_EVENT_Q (ipc_wait_event (ctx, &event, &timer), EXIT_FAILURE);
|
||||
|
||||
switch (event.type) {
|
||||
case IPC_EVENT_TYPE_CONNECTION:
|
||||
{
|
||||
cpt++;
|
||||
if (verbosity > 1) {
|
||||
printf ("connection: %d clients connected, new client is %d\n", cpt, (event.origin)->fd);
|
||||
printf ("connection: %d ctx connected, new client is %d\n", cpt, event.origin);
|
||||
}
|
||||
};
|
||||
break;
|
||||
|
@ -49,11 +60,8 @@ void main_loop ()
|
|||
{
|
||||
cpt--;
|
||||
if (verbosity > 1) {
|
||||
printf ("disconnection: %d clients remaining\n", cpt);
|
||||
printf ("disconnection: %d ctx remaining\n", cpt);
|
||||
}
|
||||
|
||||
// free the ipc_client structure
|
||||
free (event.origin);
|
||||
};
|
||||
break;
|
||||
case IPC_EVENT_TYPE_MESSAGE:
|
||||
|
@ -68,13 +76,10 @@ void main_loop ()
|
|||
}
|
||||
}
|
||||
|
||||
ret = ipc_write (event.origin, m);
|
||||
ret = ipc_write (ctx, m);
|
||||
if (ret.error_code != IPC_ERROR_NONE) {
|
||||
PRINTERR (ret, "server write");
|
||||
}
|
||||
if (verbosity > 1) {
|
||||
printf ("message sent\n");
|
||||
}
|
||||
};
|
||||
break;
|
||||
case IPC_EVENT_TYPE_TIMER:{
|
||||
|
@ -86,13 +91,17 @@ void main_loop ()
|
|||
case IPC_EVENT_TYPE_ERROR:
|
||||
{
|
||||
cpt--;
|
||||
fprintf (stderr, "a problem happened with client %d (now disconnected)", (event.origin)->fd);
|
||||
fprintf (stderr, ", %d clients remaining\n", cpt);
|
||||
|
||||
// free the ipc_client structure
|
||||
free (event.origin);
|
||||
fprintf (stderr, "a problem happened with client %d (now disconnected)", event.origin);
|
||||
fprintf (stderr, ", %d ctx remaining\n", cpt);
|
||||
};
|
||||
break;
|
||||
case IPC_EVENT_TYPE_TX:
|
||||
{
|
||||
if (verbosity > 1) {
|
||||
printf ("a message was sent\n");
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
fprintf (stderr, "there must be a problem, event not set\n");
|
||||
|
@ -108,70 +117,50 @@ void exit_program (int signal)
|
|||
{
|
||||
printf ("Quitting, signal: %d\n", signal);
|
||||
|
||||
// free remaining clients
|
||||
for (size_t i = 0; i < clients->size; i++) {
|
||||
struct ipc_connection_info *cli = clients->cinfos[i];
|
||||
if (cli != NULL) {
|
||||
free (cli);
|
||||
}
|
||||
clients->cinfos[i] = NULL;
|
||||
}
|
||||
|
||||
ipc_connections_free (clients);
|
||||
free (clients);
|
||||
|
||||
// the application will shut down, and close the service
|
||||
struct ipc_error ret = ipc_server_close (srv);
|
||||
struct ipc_error ret = ipc_close_all (ctx);
|
||||
if (ret.error_code != IPC_ERROR_NONE) {
|
||||
PRINTERR (ret, "server close");
|
||||
}
|
||||
free (srv);
|
||||
|
||||
ipc_ctx_free (ctx);
|
||||
free (ctx);
|
||||
|
||||
exit (EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
/*
|
||||
* service ping-pong: send back everything sent by the clients
|
||||
* service ping-pong: send back everything sent by the ctx
|
||||
* stop the program on SIG{TERM,INT,ALRM,USR{1,2},HUP} signals
|
||||
*/
|
||||
|
||||
int main (int argc, char *argv[], char **env)
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
argc = argc; // warnings
|
||||
argv = argv; // warnings
|
||||
|
||||
printf ("Usage: %s [verbosity]\n", argv[0]);
|
||||
if (argc > 1) {
|
||||
verbosity = atoi(argv[1]);
|
||||
}
|
||||
|
||||
printf ("pid = %d\n", getpid ());
|
||||
|
||||
srv = malloc (sizeof (struct ipc_connection_info));
|
||||
if (srv == NULL) {
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
memset (srv, 0, sizeof (struct ipc_connection_info));
|
||||
srv->type = '\0';
|
||||
srv->index = 0;
|
||||
srv->version = 0;
|
||||
srv->fd = 0;
|
||||
srv->spath = NULL;
|
||||
ctx = malloc (sizeof (struct ipc_ctx));
|
||||
memset (ctx, 0, sizeof (struct ipc_ctx));
|
||||
|
||||
struct ipc_error ret = ipc_server_init (env, srv, PONGD_SERVICE_NAME);
|
||||
struct ipc_error ret = ipc_server_init (ctx, PONGD_SERVICE_NAME);
|
||||
if (ret.error_code != IPC_ERROR_NONE) {
|
||||
PRINTERR (ret, "server init");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
printf ("Listening on %s.\n", srv->spath);
|
||||
printf ("Listening on %s.\n", ctx->cinfos[0].spath);
|
||||
|
||||
printf ("MAIN: server created\n");
|
||||
|
||||
signal (SIGHUP, exit_program);
|
||||
signal (SIGHUP, exit_program);
|
||||
signal (SIGALRM, exit_program);
|
||||
signal (SIGUSR1, exit_program);
|
||||
signal (SIGUSR2, exit_program);
|
||||
signal (SIGTERM, exit_program);
|
||||
signal (SIGINT, exit_program);
|
||||
signal (SIGINT, exit_program);
|
||||
|
||||
// the service will loop until the end of time, or a signal
|
||||
main_loop ();
|
||||
|
|
|
@ -23,52 +23,51 @@ void chomp (char *str, ssize_t len)
|
|||
}
|
||||
}
|
||||
|
||||
struct ipc_connection_info *srv;
|
||||
struct ipc_ctx *ctx = NULL;
|
||||
|
||||
void non_interactive (char *env[])
|
||||
void non_interactive ()
|
||||
{
|
||||
SECURE_DECLARATION (struct ipc_message, m);
|
||||
|
||||
// init service
|
||||
TEST_IPC_Q (ipc_connection (env, srv, SERVICE_NAME), EXIT_FAILURE);
|
||||
TEST_IPC_Q (ipc_connection (ctx, SERVICE_NAME, NULL), EXIT_FAILURE);
|
||||
TEST_IPC_Q (ipc_message_format_data (&m, 42, MSG, (ssize_t) strlen (MSG) + 1), EXIT_FAILURE);
|
||||
|
||||
printf ("msg to send (%ld): %.*s\n", (ssize_t) strlen (MSG) + 1, (int)strlen (MSG), MSG);
|
||||
TEST_IPC_Q (ipc_write (srv, &m), EXIT_FAILURE);
|
||||
// ipc_write fd: write a message without fd availability check.
|
||||
TEST_IPC_Q (ipc_write_fd (ctx->pollfd[0].fd, &m), EXIT_FAILURE);
|
||||
ipc_message_empty (&m);
|
||||
TEST_IPC_Q (ipc_read (srv, &m), EXIT_FAILURE);
|
||||
TEST_IPC_Q (ipc_read (ctx, 0 /* only valid index */, &m), EXIT_FAILURE);
|
||||
|
||||
printf ("msg recv (type: %u): %s\n", m.user_type, m.payload);
|
||||
ipc_message_empty (&m);
|
||||
|
||||
TEST_IPC_Q (ipc_close (srv), EXIT_FAILURE);
|
||||
TEST_IPC_Q (ipc_close_all (ctx), EXIT_FAILURE);
|
||||
}
|
||||
|
||||
void interactive (char *env[])
|
||||
void interactive ()
|
||||
{
|
||||
// init service
|
||||
TEST_IPC_Q (ipc_connection (env, srv, SERVICE_NAME), EXIT_FAILURE);
|
||||
TEST_IPC_Q (ipc_connection (ctx, SERVICE_NAME, NULL), EXIT_FAILURE);
|
||||
|
||||
SECURE_DECLARATION (struct ipc_event, event);
|
||||
SECURE_DECLARATION (struct ipc_connection_infos, services);
|
||||
|
||||
ipc_add (&services, srv);
|
||||
ipc_add_fd (&services, 0); // add STDIN
|
||||
ipc_add_fd (ctx, 0); // add STDIN
|
||||
|
||||
ipc_connections_print (&services);
|
||||
ipc_ctx_print (ctx);
|
||||
|
||||
long timer = 10;
|
||||
int timer = 10000;
|
||||
|
||||
while (1) {
|
||||
printf ("msg to send: ");
|
||||
fflush (stdout);
|
||||
|
||||
TEST_IPC_WAIT_EVENT_Q (ipc_wait_event (&services, NULL, &event, &timer), EXIT_FAILURE);
|
||||
TEST_IPC_WAIT_EVENT_Q (ipc_wait_event (ctx, &event, &timer), EXIT_FAILURE);
|
||||
|
||||
switch (event.type) {
|
||||
case IPC_EVENT_TYPE_TIMER:{
|
||||
printf ("time up!\n");
|
||||
timer = 10;
|
||||
timer = 10000;
|
||||
};
|
||||
break;
|
||||
case IPC_EVENT_TYPE_EXTRA_SOCKET:
|
||||
|
@ -78,7 +77,7 @@ void interactive (char *env[])
|
|||
char buf[4096];
|
||||
memset (buf, 0, 4096);
|
||||
|
||||
len = read (event.origin->fd, buf, 4096);
|
||||
len = read (event.origin, buf, 4096);
|
||||
|
||||
buf[len - 1] = '\0';
|
||||
chomp (buf, len);
|
||||
|
@ -91,9 +90,9 @@ void interactive (char *env[])
|
|||
// in case we want to quit the program
|
||||
if (len == 0 || strncmp (buf, "quit", 4) == 0 || strncmp (buf, "exit", 4) == 0) {
|
||||
|
||||
TEST_IPC_Q (ipc_close (srv), EXIT_FAILURE);
|
||||
TEST_IPC_Q (ipc_close_all (ctx), EXIT_FAILURE);
|
||||
|
||||
ipc_connections_free (&services);
|
||||
ipc_ctx_free (ctx);
|
||||
|
||||
exit (EXIT_SUCCESS);
|
||||
}
|
||||
|
@ -107,6 +106,7 @@ void interactive (char *env[])
|
|||
len = strlen (buf);
|
||||
printf ("message %lu, buffer %.*s\n", i, (int)len, buf);
|
||||
TEST_IPC_Q (ipc_message_format_data (m, 42, buf, len), EXIT_FAILURE);
|
||||
m->fd = ctx->pollfd[0].fd;
|
||||
|
||||
printf ("message from structure: %.*s\n", m->length, m->payload);
|
||||
|
||||
|
@ -114,7 +114,7 @@ void interactive (char *env[])
|
|||
printf ("\n");
|
||||
printf ("right before sending a message\n");
|
||||
#endif
|
||||
TEST_IPC_Q (ipc_write (srv, m), EXIT_FAILURE);
|
||||
TEST_IPC_Q (ipc_write (ctx, m), EXIT_FAILURE);
|
||||
#if 0
|
||||
printf ("right after sending a message\n");
|
||||
#endif
|
||||
|
@ -125,6 +125,11 @@ void interactive (char *env[])
|
|||
free (m);
|
||||
}
|
||||
break;
|
||||
case IPC_EVENT_TYPE_TX:
|
||||
{
|
||||
printf ("a message was sent\n");
|
||||
}
|
||||
break;
|
||||
case IPC_EVENT_TYPE_MESSAGE:
|
||||
{
|
||||
struct ipc_message *m = event.m;
|
||||
|
@ -141,22 +146,18 @@ void interactive (char *env[])
|
|||
}
|
||||
}
|
||||
|
||||
int main (int argc, char *argv[], char *env[])
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
argc = argc; // warnings
|
||||
argv = argv; // warnings
|
||||
// Compilers fuckery.
|
||||
argv = argv;
|
||||
|
||||
srv = malloc (sizeof (struct ipc_connection_info));
|
||||
memset (srv, 0, sizeof (struct ipc_connection_info));
|
||||
|
||||
// index and version should be filled
|
||||
srv->index = 0;
|
||||
srv->version = 0;
|
||||
ctx = malloc (sizeof (struct ipc_ctx));
|
||||
memset (ctx, 0, sizeof (struct ipc_ctx));
|
||||
|
||||
if (argc == 1)
|
||||
non_interactive (env);
|
||||
non_interactive ();
|
||||
else
|
||||
interactive (env);
|
||||
interactive ();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -64,7 +64,9 @@ void send_receive (int sockfd)
|
|||
// 2 | 6 | 0 | "coucou"
|
||||
// 1 B | 4 B | 1 | 6 B
|
||||
ipc_message_raw_serialize ((char *)buf, MSG_TYPE_DATA, 42, "coucou", 6);
|
||||
print_hexa ("WAITING 10 seconds then message to send", buf, 12);
|
||||
printf("\n");
|
||||
print_hexa ("message to send", buf, 12);
|
||||
printf("\n");
|
||||
// sleep (1);
|
||||
T_PERROR_Q ((send (sockfd, buf, 12, 0) == -1), "sending a message", EXIT_FAILURE);
|
||||
printf ("message 'coucou' sent\n");
|
||||
|
@ -72,6 +74,7 @@ void send_receive (int sockfd)
|
|||
|
||||
// receiving a message
|
||||
T_PERROR_Q (((paylen = recv (sockfd, buf, BUFSIZ, 0)) < 0), "receiving a message", EXIT_FAILURE);
|
||||
printf("\n");
|
||||
|
||||
if (paylen == 0) {
|
||||
fprintf (stderr, "error: disconnection from the server\n");
|
||||
|
@ -96,7 +99,7 @@ int main (int argc, char *argv[])
|
|||
|
||||
send_receive (sockfd);
|
||||
|
||||
printf ("Disconnection\n");
|
||||
printf ("\nDisconnection\n");
|
||||
|
||||
// close the socket
|
||||
close (sockfd);
|
||||
|
|
|
@ -42,7 +42,7 @@ void chomp (char *str, size_t len)
|
|||
else
|
||||
connection from the client:
|
||||
1. client sends service name
|
||||
2. networkd establishes a connection to the service
|
||||
2. ipcd establishes a connection to the service
|
||||
3. ack
|
||||
else
|
||||
lolwat shouldn't happen :(
|
||||
|
@ -52,26 +52,26 @@ void chomp (char *str, size_t len)
|
|||
|
||||
#define SERVICE_NAME "simpletcp"
|
||||
|
||||
struct networkd *ctx;
|
||||
struct ipc_ctx *ctx = NULL;
|
||||
|
||||
void handle_disconnection (int fd)
|
||||
{
|
||||
int delfd;
|
||||
|
||||
delfd = ipc_switching_del (ctx->TCP_TO_IPC, fd);
|
||||
delfd = ipc_switching_del (&ctx->switchdb, fd);
|
||||
if (delfd >= 0) {
|
||||
close (delfd);
|
||||
ipc_del_fd (ctx->clients, delfd);
|
||||
ipc_del_fd (ctx, delfd);
|
||||
}
|
||||
|
||||
close (fd);
|
||||
ipc_del_fd (ctx->clients, fd);
|
||||
ipc_del_fd (ctx, fd);
|
||||
|
||||
// printf ("TCP_TO_IPC\n");
|
||||
ipc_switching_print (ctx->TCP_TO_IPC);
|
||||
// printf ("ctx.switchdb\n");
|
||||
ipc_switching_print (&ctx->switchdb);
|
||||
}
|
||||
|
||||
void tcp_connection (char **env, int fd)
|
||||
void tcp_connection (int fd)
|
||||
{
|
||||
SECURE_BUFFER_DECLARATION (char, buf, BUFSIZ);
|
||||
|
||||
|
@ -89,16 +89,12 @@ void tcp_connection (char **env, int fd)
|
|||
// TODO: tests
|
||||
T_PERROR_Q ((send (fd, "OK", 2, 0) <= 0), "sending a message", EXIT_FAILURE);
|
||||
|
||||
SECURE_DECLARATION (struct ipc_connection_info, tcp_to_ipc_ci);
|
||||
|
||||
struct ipc_error ret = ipc_connection (env, &tcp_to_ipc_ci, buf);
|
||||
printf ("connection to %s\n", buf);
|
||||
struct ipc_error ret = ipc_connection_switched (ctx, buf, fd, NULL);
|
||||
if (ret.error_code != IPC_ERROR_NONE) {
|
||||
fprintf (stderr, "%s\n", ret.error_message);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
ipc_switching_add (ctx->TCP_TO_IPC, fd, tcp_to_ipc_ci.fd);
|
||||
ipc_add_fd (ctx->clients, tcp_to_ipc_ci.fd);
|
||||
}
|
||||
|
||||
int accept_new_client (int serverfd)
|
||||
|
@ -111,13 +107,14 @@ int accept_new_client (int serverfd)
|
|||
accept (serverfd, (struct sockaddr *)&client, &addrlen)) == -1), "accept new client",
|
||||
EXIT_FAILURE);
|
||||
|
||||
// adding a client
|
||||
ipc_add_fd (ctx->clients, sock_fd_client);
|
||||
// adding a client, for now not switched:
|
||||
// tcpd should handle the first message (getting the service name)
|
||||
ipc_add_fd (ctx, sock_fd_client);
|
||||
|
||||
return sock_fd_client;
|
||||
}
|
||||
|
||||
void main_loop (int argc, char **argv, char **env)
|
||||
void main_loop (int argc, char **argv)
|
||||
{
|
||||
argc = argc; // FIXME: useless
|
||||
int serverfd;
|
||||
|
@ -155,80 +152,89 @@ void main_loop (int argc, char **argv, char **env)
|
|||
return;
|
||||
}
|
||||
|
||||
SECURE_BUFFER_HEAP_ALLOCATION_Q (ctx->clients, sizeof (struct ipc_connection_infos),, EXIT_FAILURE);
|
||||
SECURE_DECLARATION (struct ipc_event, event);
|
||||
|
||||
ipc_add_fd (ctx->clients, serverfd);
|
||||
ipc_add_fd (ctx, serverfd);
|
||||
|
||||
int cpt = 0;
|
||||
|
||||
int timer = 10000;
|
||||
while (1) {
|
||||
// ipc_wait_event provides one event at a time
|
||||
// warning: event->m is free'ed if not NULL
|
||||
long timer = 10;
|
||||
|
||||
TEST_IPC_WAIT_EVENT_Q (ipc_wait_event_networkd (ctx->clients, ctx->srv, &event, ctx->TCP_TO_IPC, &timer)
|
||||
, EXIT_FAILURE);
|
||||
ipc_ctx_print (ctx);
|
||||
TEST_IPC_WAIT_EVENT_Q (ipc_wait_event (ctx, &event, &timer), EXIT_FAILURE);
|
||||
|
||||
switch (event.type) {
|
||||
case IPC_EVENT_TYPE_TIMER:{
|
||||
printf ("timed out!\n");
|
||||
|
||||
timer = 10;
|
||||
timer = 10000;
|
||||
}
|
||||
break;
|
||||
|
||||
case IPC_EVENT_TYPE_SWITCH:{
|
||||
printf ("switch happened\n");
|
||||
printf ("switch happened, from %d\n", event.origin);
|
||||
}
|
||||
break;
|
||||
|
||||
case IPC_EVENT_TYPE_EXTRA_SOCKET:
|
||||
{
|
||||
// NEW CLIENT
|
||||
if (event.origin->fd == serverfd) {
|
||||
if (event.origin == serverfd) {
|
||||
int sock_fd_client = accept_new_client (serverfd);
|
||||
ctx->cpt++;
|
||||
printf ("TCP connection: %d clients connected\n", ctx->cpt);
|
||||
cpt++;
|
||||
printf ("TCP connection: %d ctx connected\n", cpt);
|
||||
printf ("new TCP client has the fd %d\n", sock_fd_client);
|
||||
}
|
||||
// CLIENT IS TALKING
|
||||
else {
|
||||
tcp_connection (env, event.origin->fd);
|
||||
// Test: if the socket already is in the switch, this means we can just switch the packet.
|
||||
// Is the socket in the switch db?
|
||||
tcp_connection (event.origin);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case IPC_EVENT_TYPE_CONNECTION:
|
||||
{
|
||||
ctx->cpt++;
|
||||
printf ("connection: %d clients connected\n", ctx->cpt);
|
||||
printf ("new client has the fd %d\n", (event.origin)->fd);
|
||||
cpt++;
|
||||
printf ("connection: %d ctx connected\n", cpt);
|
||||
printf ("new client has the fd %d\n", event.origin);
|
||||
};
|
||||
break;
|
||||
|
||||
case IPC_EVENT_TYPE_DISCONNECTION:
|
||||
{
|
||||
ctx->cpt--;
|
||||
printf ("disconnection: %d clients remaining\n", ctx->cpt);
|
||||
|
||||
// free the ipc_client structure
|
||||
// if (event.origin != NULL)
|
||||
// free (event.origin);
|
||||
cpt--;
|
||||
printf ("disconnection: %d ctx remaining\n", cpt);
|
||||
};
|
||||
break;
|
||||
|
||||
case IPC_EVENT_TYPE_MESSAGE:
|
||||
{
|
||||
struct ipc_message *m = event.m;
|
||||
if (m->length > 0) {
|
||||
printf ("message received (type %d): %.*s\n", m->type, m->length, m->payload);
|
||||
}
|
||||
|
||||
TEST_IPC_P (ipc_write (event.origin, m), "server write");
|
||||
// m->fd = 3;
|
||||
TEST_IPC_P (ipc_write (ctx, m), "server write");
|
||||
};
|
||||
break;
|
||||
case IPC_EVENT_TYPE_ERROR:
|
||||
fprintf (stderr, "a problem happened with client %d\n", (event.origin)->fd);
|
||||
|
||||
case IPC_EVENT_TYPE_TX:
|
||||
{
|
||||
printf ("a message was sent\n");
|
||||
}
|
||||
break;
|
||||
|
||||
case IPC_EVENT_TYPE_ERROR:
|
||||
fprintf (stderr, "a problem happened with client %d\n", event.origin);
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf (stderr, "there must be a problem, event not set\n");
|
||||
fprintf (stderr, "there must be a problem, event not set: %d\n", event.type);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -240,36 +246,22 @@ void exit_program (int signal)
|
|||
{
|
||||
printf ("Quitting, signal: %d\n", signal);
|
||||
|
||||
// free remaining clients
|
||||
for (size_t i = 0; i < ctx->clients->size; i++) {
|
||||
struct ipc_connection_info *cli = ctx->clients->cinfos[i];
|
||||
if (cli != NULL) {
|
||||
free (cli);
|
||||
}
|
||||
ctx->clients->cinfos[i] = NULL;
|
||||
}
|
||||
|
||||
ipc_connections_free (ctx->clients);
|
||||
|
||||
// the application will shut down, and close the service
|
||||
TEST_IPC_P (ipc_server_close (ctx->srv), "server close");
|
||||
// Close then free remaining ctx.
|
||||
ipc_close_all (ctx);
|
||||
ipc_ctx_free (ctx);
|
||||
|
||||
// free, free everything!
|
||||
free (ctx->clients);
|
||||
free (ctx->srv);
|
||||
free (ctx->TCP_TO_IPC->collection);
|
||||
free (ctx->TCP_TO_IPC);
|
||||
free (ctx);
|
||||
|
||||
exit (EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
/*
|
||||
* service ping-pong: send back everything sent by the clients
|
||||
* service ping-pong: send back everything sent by the ctx
|
||||
* stop the program on SIG{TERM,INT,ALRM,USR{1,2},HUP} signals
|
||||
*/
|
||||
|
||||
int main (int argc, char *argv[], char **env)
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
// check the number of args on command line
|
||||
if (argc != 2) {
|
||||
|
@ -279,17 +271,15 @@ int main (int argc, char *argv[], char **env)
|
|||
|
||||
printf ("pid = %d\n", getpid ());
|
||||
|
||||
SECURE_BUFFER_HEAP_ALLOCATION_Q (ctx, sizeof (struct networkd) ,, EXIT_FAILURE);
|
||||
SECURE_BUFFER_HEAP_ALLOCATION_Q (ctx->TCP_TO_IPC, sizeof (struct ipc_switchings) ,, EXIT_FAILURE);
|
||||
SECURE_BUFFER_HEAP_ALLOCATION_Q (ctx->TCP_TO_IPC->collection, sizeof (struct ipc_switching) ,, EXIT_FAILURE);
|
||||
SECURE_BUFFER_HEAP_ALLOCATION_Q (ctx->srv, sizeof (struct ipc_connection_info),, EXIT_FAILURE);
|
||||
ctx = malloc (sizeof (struct ipc_ctx));
|
||||
memset(ctx, 0, sizeof (struct ipc_ctx));
|
||||
|
||||
struct ipc_error ret = ipc_server_init (env, ctx->srv, SERVICE_NAME);
|
||||
struct ipc_error ret = ipc_server_init (ctx, SERVICE_NAME);
|
||||
if (ret.error_code != IPC_ERROR_NONE) {
|
||||
fprintf (stderr, "%s\n", ret.error_message);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
printf ("Listening on [%s].\n", ctx->srv->spath);
|
||||
printf ("Listening on [%s].\n", ctx->cinfos[0].spath);
|
||||
|
||||
printf ("MAIN: server created\n");
|
||||
|
||||
|
@ -301,7 +291,7 @@ int main (int argc, char *argv[], char **env)
|
|||
signal (SIGINT , exit_program);
|
||||
|
||||
// the service will loop until the end of time, or a signal
|
||||
main_loop (argc, argv, env);
|
||||
main_loop (argc, argv);
|
||||
|
||||
// main_loop should not return
|
||||
return EXIT_FAILURE;
|
||||
|
|
|
@ -6,30 +6,27 @@
|
|||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
SECURE_DECLARATION (struct ipc_error, ret);
|
||||
SECURE_DECLARATION (struct ipc_connection_info, srv);
|
||||
|
||||
if (argc != 2) {
|
||||
fprintf (stderr, "usage: %s service_name\n", argv[0]);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
SECURE_DECLARATION (struct ipc_error, ret);
|
||||
int fd = 0;
|
||||
|
||||
char *service_name = argv[1];
|
||||
|
||||
ret = ipc_contact_networkd (&srv, service_name);
|
||||
ret = ipc_contact_ipcd (&fd, service_name);
|
||||
|
||||
printf ("ret = %d\n", ret.error_code);
|
||||
|
||||
if (ret.error_code == IPC_ERROR_NONE && srv.fd != 0) {
|
||||
if (ret.error_code == IPC_ERROR_NONE && fd > 0) {
|
||||
printf ("Success\n");
|
||||
} else {
|
||||
printf ("Ow. :(\n");
|
||||
}
|
||||
|
||||
usock_close (srv.fd);
|
||||
usock_close (fd);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -4,35 +4,32 @@
|
|||
|
||||
#include "../src/ipc.h"
|
||||
|
||||
int main (int argc, char *argv[], char *env[])
|
||||
int main (void)
|
||||
{
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
SECURE_DECLARATION (struct ipc_error, ret);
|
||||
SECURE_DECLARATION (struct ipc_connection_info, srv);
|
||||
SECURE_DECLARATION (struct ipc_connection_info, client);
|
||||
SECURE_DECLARATION (struct ipc_connection_info, contacted_service);
|
||||
SECURE_DECLARATION (struct ipc_ctx, ctx);
|
||||
SECURE_DECLARATION (struct ipc_event, event);
|
||||
|
||||
// service start
|
||||
TEST_IPC_Q (ipc_server_init (env, &srv, "network"), EXIT_FAILURE);
|
||||
TEST_IPC_Q (ipc_server_init (&ctx, "ipcd"), EXIT_FAILURE);
|
||||
|
||||
printf ("service initialized, waiting for a client\n");
|
||||
|
||||
// accept a new client
|
||||
TEST_IPC_Q (ipc_accept (&srv, &client), EXIT_FAILURE);
|
||||
TEST_IPC_Q (ipc_accept_add (&event, &ctx, 0 /* the only valid index right now */), EXIT_FAILURE);
|
||||
int client_fd = ctx.pollfd[ctx.size-1].fd;
|
||||
|
||||
// TODO: read a message to know the requested service
|
||||
SECURE_DECLARATION (struct ipc_message, msg);
|
||||
TEST_IPC_Q (ipc_read (&client, &msg), EXIT_FAILURE);
|
||||
TEST_IPC_Q (ipc_read (&ctx, 1 /* 1 = our client */, &msg), EXIT_FAILURE);
|
||||
printf ("received message: %s\n", msg.payload);
|
||||
|
||||
/** TODO: contact the service */
|
||||
printf ("WARNING: currently this program only ask for pong service %d\n", ret.error_code);
|
||||
TEST_IPC_Q (ipc_connection (env, &contacted_service, "pong"), EXIT_FAILURE);
|
||||
TEST_IPC_Q (ipc_connection (&ctx, "pong", NULL), EXIT_FAILURE);
|
||||
|
||||
ipc_provide_fd (client.fd, contacted_service.fd);
|
||||
ipc_provide_fd (client_fd, ctx.pollfd[ctx.size-1].fd);
|
||||
|
||||
TEST_IPC_Q (ipc_server_close (&srv), EXIT_FAILURE);
|
||||
TEST_IPC_Q (ipc_close_all (&ctx), EXIT_FAILURE);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -19,12 +19,13 @@ void chomp (char *str, ssize_t len)
|
|||
}
|
||||
}
|
||||
|
||||
struct ipc_connection_info *srv;
|
||||
struct ipc_ctx *ctx;
|
||||
|
||||
void interactive (char *env[])
|
||||
void interactive ()
|
||||
{
|
||||
long timer = 10;
|
||||
int timer = 10000; // 10 seconds
|
||||
|
||||
SECURE_DECLARATION (struct ipc_event, event);
|
||||
SECURE_BUFFER_DECLARATION (char, service_name, 100);
|
||||
|
||||
char *sn = getenv ("PATH_TRANSLATED");
|
||||
|
@ -37,21 +38,17 @@ void interactive (char *env[])
|
|||
}
|
||||
|
||||
// init service
|
||||
TEST_IPC_Q (ipc_connection (env, srv, service_name), EXIT_FAILURE);
|
||||
TEST_IPC_Q (ipc_connection (ctx, service_name, NULL), EXIT_FAILURE);
|
||||
|
||||
SECURE_DECLARATION (struct ipc_event, event);
|
||||
SECURE_DECLARATION (struct ipc_connection_infos, services);
|
||||
|
||||
ipc_add (&services, srv);
|
||||
ipc_add_fd (&services, 0); // add STDIN
|
||||
ipc_add_fd (ctx, 0); // add STDIN
|
||||
|
||||
while (1) {
|
||||
TEST_IPC_WAIT_EVENT_Q (ipc_wait_event (&services, NULL, &event, &timer), EXIT_FAILURE);
|
||||
TEST_IPC_WAIT_EVENT_Q (ipc_wait_event (ctx, &event, &timer), EXIT_FAILURE);
|
||||
|
||||
switch (event.type) {
|
||||
case IPC_EVENT_TYPE_TIMER:{
|
||||
fprintf (stderr, "time up!\n");
|
||||
timer = 10;
|
||||
timer = 10000;
|
||||
};
|
||||
break;
|
||||
case IPC_EVENT_TYPE_EXTRA_SOCKET:
|
||||
|
@ -60,19 +57,19 @@ void interactive (char *env[])
|
|||
SECURE_BUFFER_DECLARATION (char, buf, 4096);
|
||||
ssize_t len;
|
||||
|
||||
len = read (event.origin->fd, buf, 4096);
|
||||
len = read (event.origin, buf, 4096);
|
||||
|
||||
// in case we want to quit the program
|
||||
if (len == 0 || strncmp (buf, "quit", 4) == 0 || strncmp (buf, "exit", 4) == 0) {
|
||||
|
||||
TEST_IPC_Q (ipc_close (srv), EXIT_FAILURE);
|
||||
TEST_IPC_Q (ipc_close_all (ctx), EXIT_FAILURE);
|
||||
|
||||
ipc_connections_free (&services);
|
||||
ipc_ctx_free (ctx);
|
||||
|
||||
exit (EXIT_SUCCESS);
|
||||
}
|
||||
// send the message read on STDIN
|
||||
ssize_t len_sent = write (srv->fd, buf, len);
|
||||
ssize_t len_sent = write (ctx->pollfd[0].fd, buf, len);
|
||||
if (len_sent != len) {
|
||||
fprintf (stderr, "cannot send the message %lu-byte message, sent %lu bytes",
|
||||
len, len_sent);
|
||||
|
@ -94,6 +91,10 @@ void interactive (char *env[])
|
|||
fflush (stdout);
|
||||
};
|
||||
break;
|
||||
case IPC_EVENT_TYPE_TX: {
|
||||
printf ("Message sent\n");
|
||||
}
|
||||
break;
|
||||
ERROR_CASE (IPC_EVENT_TYPE_DISCONNECTION, "main loop", "disconnection: should not happen");
|
||||
ERROR_CASE (IPC_EVENT_TYPE_NOT_SET , "main loop", "not set: should not happen");
|
||||
ERROR_CASE (IPC_EVENT_TYPE_CONNECTION , "main loop", "connection: should not happen");
|
||||
|
@ -104,19 +105,12 @@ void interactive (char *env[])
|
|||
}
|
||||
}
|
||||
|
||||
int main (int argc, char *argv[], char *env[])
|
||||
int main (void)
|
||||
{
|
||||
argc = argc; // warnings
|
||||
argv = argv; // warnings
|
||||
ctx = malloc (sizeof (struct ipc_ctx));
|
||||
memset (ctx, 0, sizeof (struct ipc_ctx));
|
||||
|
||||
srv = malloc (sizeof (struct ipc_connection_info));
|
||||
memset (srv, 0, sizeof (struct ipc_connection_info));
|
||||
|
||||
// index and version should be filled
|
||||
srv->index = 0;
|
||||
srv->version = 0;
|
||||
|
||||
interactive (env);
|
||||
interactive ();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,183 @@
|
|||
.\" Generated by scdoc 1.9.6
|
||||
.ie \n(.g .ds Aq \(aq
|
||||
.el .ds Aq '
|
||||
.nh
|
||||
.ad l
|
||||
.\" Begin generated content:
|
||||
.TH "libipc" "7" "2020-12-08"
|
||||
.P
|
||||
.SH NAME
|
||||
.P
|
||||
libipc - Simple, easy-to-use IPC library
|
||||
.P
|
||||
.SH DESCRIPTION
|
||||
.P
|
||||
\fB\fRlibipc\fB\fR is a library that provides interprocess communication medium between applications.
|
||||
It provides both client and server code.
|
||||
.P
|
||||
.SH SYNOPSIS
|
||||
.P
|
||||
\fB\fR#include <ipc.h>\fB\fR
|
||||
.P
|
||||
.SS Initialization, exchanges, disconnection
|
||||
.P
|
||||
// server initialization
|
||||
.P
|
||||
\fIenum ipc_errors\fR \fB\fRipc_server_init\fB\fR (\fBchar\fR **env, \fBconst char\fR *sname);
|
||||
.P
|
||||
// connection establishement to a server
|
||||
.P
|
||||
\fIenum ipc_errors\fR \fB\fRipc_connection\fB\fR (\fBchar\fR **env, \fBconst char\fR *, int *serverfd);
|
||||
.P
|
||||
// closing a server
|
||||
.P
|
||||
\fIenum ipc_errors\fR \fB\fRipc_close\fB\fR (\fBstruct ipc_connection_info\fR *srv);
|
||||
.P
|
||||
// closing a connection
|
||||
.P
|
||||
\fIenum ipc_errors\fR \fB\fRipc_close\fB\fR (\fBstruct ipc_connection_info\fR *p);
|
||||
.br
|
||||
\fIenum ipc_errors\fR \fB\fRipc_accept\fB\fR (\fBstruct ipc_connection_info\fR *srv, \fBstruct ipc_connection_info\fR *p);
|
||||
.P
|
||||
\fIenum ipc_errors\fR \fB\fRipc_read\fB\fR (\fBconst struct ipc_connection_info\fR *, \fBstruct ipc_message\fR *m);
|
||||
.br
|
||||
\fIenum ipc_errors\fR \fB\fRipc_write\fB\fR (\fBconst struct ipc_connection_info\fR *, \fBconst struct ipc_message\fR *m);
|
||||
.br
|
||||
\fIenum ipc_errors\fR \fB\fRipc_wait_event\fB\fR (\fBstruct ipc_ctx\fR *clients, \fBstruct ipc_connection_info\fR *srv, \fBstruct ipc_event\fR *event);
|
||||
.P
|
||||
.P
|
||||
// store and remove only pointers on allocated structures
|
||||
.P
|
||||
\fIenum ipc_errors\fR \fB\fRipc_add\fB\fR (\fBstruct ipc_ctx\fR *cinfos, \fBstruct ipc_connection_info\fR *cinfo);
|
||||
.br
|
||||
\fIenum ipc_errors\fR \fB\fRipc_del\fB\fR (\fBstruct ipc_ctx\fR *cinfos, \fBstruct ipc_connection_info\fR *cinfo);
|
||||
.P
|
||||
// add an arbitrary file descriptor to read
|
||||
.P
|
||||
\fIenum ipc_errors\fR \fB\fRipc_add_fd\fB\fR (\fBstruct ipc_ctx\fR *cinfos, \fBint\fR fd);
|
||||
.P
|
||||
.P
|
||||
.SS Message functions
|
||||
.P
|
||||
// create msg structure from buffer
|
||||
.P
|
||||
\fIenum ipc_errors\fR \fB\fRipc_message_format_read\fB\fR (\fBstruct ipc_message\fR *m, \fBconst char\fR *buf, \fBssize_t\fR msize);
|
||||
.P
|
||||
// create buffer from msg structure
|
||||
.P
|
||||
\fIenum ipc_errors\fR \fB\fRipc_message_format_write\fB\fR (\fBconst struct ipc_message\fR *m, \fBchar\fR **buf, \fBssize_t\fR *msize);
|
||||
.P
|
||||
\fIenum ipc_errors\fR \fB\fRipc_message_format\fB\fR (\fBstruct ipc_message\fR *m, \fBchar\fR type, \fBconst char\fR *payload, \fBssize_t\fR length);
|
||||
.br
|
||||
\fIenum ipc_errors\fR \fB\fRipc_message_format_data\fB\fR (\fBstruct ipc_message\fR *m, \fBconst char\fR *payload, \fBssize_t\fR length);
|
||||
.br
|
||||
\fIenum ipc_errors\fR \fB\fRipc_message_format_server_close\fB\fR (\fBstruct ipc_message\fR *m);
|
||||
.P
|
||||
\fIenum ipc_errors\fR \fB\fRipc_message_empty\fB\fR (\fBstruct ipc_message\fR *m);
|
||||
.P
|
||||
.P
|
||||
.SH STRUCTURES
|
||||
.P
|
||||
.nf
|
||||
.RS 4
|
||||
struct ipc_connection_info {
|
||||
uint32_t version;
|
||||
uint32_t index;
|
||||
int32_t fd;
|
||||
char type; // may be an arbitrary fd
|
||||
char *spath; // max size: PATH_MAX, used to store unix socket path
|
||||
};
|
||||
|
||||
struct ipc_ctx {
|
||||
struct ipc_connection_info ** cinfos;
|
||||
int32_t size;
|
||||
};
|
||||
|
||||
struct ipc_message {
|
||||
char type;
|
||||
uint32_t length;
|
||||
char *payload;
|
||||
};
|
||||
|
||||
struct ipc_event {
|
||||
enum ipc_event_type type;
|
||||
void* origin; // currently used as an client or service pointer
|
||||
void* m; // message pointer
|
||||
};
|
||||
.fi
|
||||
.RE
|
||||
.P
|
||||
.P
|
||||
.SH ENUMERATIONS
|
||||
.P
|
||||
.nf
|
||||
.RS 4
|
||||
enum msg_types {
|
||||
MSG_TYPE_SERVER_CLOSE = 0
|
||||
, MSG_TYPE_ERR
|
||||
, MSG_TYPE_DATA
|
||||
} message_types;
|
||||
.fi
|
||||
.RE
|
||||
.P
|
||||
Function \fB\fRipc_wait_event\fB\fR returns an \fBevent type\fR structure.
|
||||
The event may be a (dis)connection, received data or an error.
|
||||
It also can be \fBIPC_EVENT_TYPE_EXTRA_SOCKET\fR since an arbitrary file descriptor can be added to the \fBipc_ctx\fR structure with \fB\fRipc_add_fd\fB\fR.
|
||||
.P
|
||||
.nf
|
||||
.RS 4
|
||||
enum ipc_event_type {
|
||||
IPC_EVENT_TYPE_NOT_SET
|
||||
, IPC_EVENT_TYPE_ERROR
|
||||
, IPC_EVENT_TYPE_EXTRA_SOCKET
|
||||
, IPC_EVENT_TYPE_CONNECTION
|
||||
, IPC_EVENT_TYPE_DISCONNECTION
|
||||
, IPC_EVENT_TYPE_MESSAGE
|
||||
};
|
||||
|
||||
enum ipc_errors {
|
||||
\&.\&.\&.
|
||||
};
|
||||
.fi
|
||||
.RE
|
||||
.P
|
||||
.P
|
||||
.SH EXAMPLES
|
||||
.P
|
||||
Examples are available in the \fB/examples\fR directory.
|
||||
.P
|
||||
.SH NOTES
|
||||
.P
|
||||
.SH SEE ALSO
|
||||
.P
|
||||
.SH BUGS & LIMITATIONS
|
||||
.P
|
||||
.RS 4
|
||||
.ie n \{\
|
||||
\h'-04'\(bu\h'+03'\c
|
||||
.\}
|
||||
.el \{\
|
||||
.IP \(bu 4
|
||||
.\}
|
||||
Documentation is currently limited.
|
||||
.RE
|
||||
.RS 4
|
||||
.ie n \{\
|
||||
\h'-04'\(bu\h'+03'\c
|
||||
.\}
|
||||
.el \{\
|
||||
.IP \(bu 4
|
||||
.\}
|
||||
Tests are currently limited.
|
||||
.RE
|
||||
.RS 4
|
||||
.ie n \{\
|
||||
\h'-04'\(bu\h'+03'\c
|
||||
.\}
|
||||
.el \{\
|
||||
.IP \(bu 4
|
||||
.\}
|
||||
No code audit has been made.
|
||||
|
||||
.RE
|
||||
.P
|
|
@ -17,15 +17,15 @@ It provides both client and server code.
|
|||
|
||||
// server initialization
|
||||
|
||||
_enum ipc_errors_ **ipc_server_init** (*char* \*\*env , *struct ipc_connection_info* \*srv, *const char* \*sname);
|
||||
_enum ipc_errors_ **ipc_server_init** (*char* \*\*env, *const char* \*sname);
|
||||
|
||||
// connection establishement to a server
|
||||
|
||||
_enum ipc_errors_ **ipc_connection** (*char* \*\*env, *struct ipc_connection_info* \*, *const char* \*);
|
||||
_enum ipc_errors_ **ipc_connection** (*char* \*\*env, *const char* \*, int \*serverfd);
|
||||
|
||||
// closing a server
|
||||
|
||||
_enum ipc_errors_ **ipc_server_close** (*struct ipc_connection_info* \*srv);
|
||||
_enum ipc_errors_ **ipc_close** (*struct ipc_connection_info* \*srv);
|
||||
|
||||
// closing a connection
|
||||
|
||||
|
@ -34,17 +34,17 @@ _enum ipc_errors_ **ipc_accept** (*struct ipc_connection_info* \*srv, *struct ip
|
|||
|
||||
_enum ipc_errors_ **ipc_read** (*const struct ipc_connection_info* \*, *struct ipc_message* \*m);++
|
||||
_enum ipc_errors_ **ipc_write** (*const struct ipc_connection_info* \*, *const struct ipc_message* \*m);++
|
||||
_enum ipc_errors_ **ipc_wait_event** (*struct ipc_connection_infos* \*clients, *struct ipc_connection_info* \*srv, *struct ipc_event* \*event);
|
||||
_enum ipc_errors_ **ipc_wait_event** (*struct ipc_ctx* \*clients, *struct ipc_connection_info* \*srv, *struct ipc_event* \*event);
|
||||
|
||||
|
||||
// store and remove only pointers on allocated structures
|
||||
|
||||
_enum ipc_errors_ **ipc_add** (*struct ipc_connection_infos* \*cinfos, *struct ipc_connection_info* \*cinfo);++
|
||||
_enum ipc_errors_ **ipc_del** (*struct ipc_connection_infos* \*cinfos, *struct ipc_connection_info* \*cinfo);
|
||||
_enum ipc_errors_ **ipc_add** (*struct ipc_ctx* \*cinfos, *struct ipc_connection_info* \*cinfo);++
|
||||
_enum ipc_errors_ **ipc_del** (*struct ipc_ctx* \*cinfos, *struct ipc_connection_info* \*cinfo);
|
||||
|
||||
// add an arbitrary file descriptor to read
|
||||
|
||||
_enum ipc_errors_ **ipc_add_fd** (*struct ipc_connection_infos* \*cinfos, *int* fd);
|
||||
_enum ipc_errors_ **ipc_add_fd** (*struct ipc_ctx* \*cinfos, *int* fd);
|
||||
|
||||
|
||||
## Message functions
|
||||
|
@ -75,7 +75,7 @@ _enum ipc_errors_ **ipc_message_empty** (*struct ipc_message* \*m);
|
|||
char *spath; // max size: PATH_MAX, used to store unix socket path
|
||||
};
|
||||
|
||||
struct ipc_connection_infos {
|
||||
struct ipc_ctx {
|
||||
struct ipc_connection_info ** cinfos;
|
||||
int32_t size;
|
||||
};
|
||||
|
@ -106,7 +106,7 @@ _enum ipc_errors_ **ipc_message_empty** (*struct ipc_message* \*m);
|
|||
|
||||
Function **ipc_wait_event** returns an *event type* structure.\
|
||||
The event may be a (dis)connection, received data or an error.\
|
||||
It also can be *IPC_EVENT_TYPE_EXTRA_SOCKET* since an arbitrary file descriptor can be added to the *ipc_connection_infos* structure with **ipc_add_fd**.
|
||||
It also can be *IPC_EVENT_TYPE_EXTRA_SOCKET* since an arbitrary file descriptor can be added to the *ipc_ctx* structure with **ipc_add_fd**.
|
||||
|
||||
```
|
||||
enum ipc_event_type {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
package=libipc # Package name.
|
||||
version=0.6.0 # Package version.
|
||||
version=0.7.2 # Package version.
|
||||
|
||||
# Our targets are the library and its documentation.
|
||||
targets=(libipc man/libipc.7)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,48 @@
|
|||
#include "ipc.h"
|
||||
|
||||
/**
|
||||
* PERFORMANCE POINT:
|
||||
* Realloc is performed at each new user. There is plenty of room for improvement,
|
||||
* for example by managing allocations of thousands of structures at once.
|
||||
* WARNING: Store and remove only pointers on allocated structures.
|
||||
*/
|
||||
struct ipc_error ipc_ctx_new_alloc (struct ipc_ctx *ctx)
|
||||
{
|
||||
ctx->size++;
|
||||
|
||||
// Memory could be not allocated, yet.
|
||||
if (ctx->size == 1 && ctx->cinfos == NULL && ctx->pollfd == NULL) {
|
||||
SECURE_BUFFER_HEAP_ALLOCATION_R (ctx->cinfos, sizeof (struct ipc_connection_info),,
|
||||
IPC_ERROR_ADD__MALLOC);
|
||||
SECURE_BUFFER_HEAP_ALLOCATION_R (ctx->pollfd, sizeof (struct pollfd),, IPC_ERROR_ADD__MALLOC_POLLFD);
|
||||
} else {
|
||||
ctx->cinfos = realloc (ctx->cinfos, sizeof (struct ipc_connection_info) * ctx->size);
|
||||
ctx->pollfd = realloc (ctx->pollfd, sizeof (struct pollfd ) * ctx->size);
|
||||
}
|
||||
|
||||
T_R ((ctx->cinfos == NULL), IPC_ERROR_ADD__EMPTY_LIST);
|
||||
T_R ((ctx->pollfd == NULL), IPC_ERROR_ADD__EMPTY_LIST);
|
||||
|
||||
// Clean the last entry.
|
||||
memset (&ctx->cinfos[ctx->size -1], 0, sizeof (struct ipc_connection_info));
|
||||
memset (&ctx->pollfd[ctx->size -1], 0, sizeof (struct pollfd));
|
||||
|
||||
IPC_RETURN_NO_ERROR;
|
||||
}
|
||||
|
||||
void ipc_ctx_free (struct ipc_ctx *ctx)
|
||||
{
|
||||
if (ctx->cinfos != NULL) {
|
||||
free (ctx->cinfos);
|
||||
ctx->cinfos = NULL;
|
||||
}
|
||||
if (ctx->pollfd != NULL) {
|
||||
free (ctx->pollfd);
|
||||
ctx->pollfd = NULL;
|
||||
}
|
||||
ctx->size = 0;
|
||||
|
||||
ipc_switching_free(&ctx->switchdb);
|
||||
|
||||
ipc_messages_free (&ctx->tx);
|
||||
}
|
36
src/error.c
36
src/error.c
|
@ -12,22 +12,15 @@ static struct ipc_errors_verbose ipc_errors_verbose[] = {
|
|||
{IPC_ERROR_NONE, "no error"}
|
||||
, {IPC_ERROR_CLOSED_RECIPIENT, "closed recipient"}
|
||||
|
||||
, {IPC_ERROR_SERVER_INIT__NO_DIR_CANNOT_CREATE_IT,
|
||||
"ipc_server_init: no directory for ipc and cannot to create it"}
|
||||
, {IPC_ERROR_SERVER_INIT__NON_WRITABLE_DIR, "ipc_server_init: non writable directory for ipc"}
|
||||
|
||||
, {IPC_ERROR_SERVER_INIT__NO_ENVIRONMENT_PARAM, "ipc_server_init: no environment param"}
|
||||
, {IPC_ERROR_SERVER_INIT__NO_SERVICE_PARAM , "ipc_server_init: no service param"}
|
||||
, {IPC_ERROR_SERVER_INIT__NO_SERVER_NAME_PARAM, "ipc_server_init: no server name param"}
|
||||
, {IPC_ERROR_SERVER_INIT__MALLOC , "ipc_server_init: error on malloc function"}
|
||||
|
||||
, {IPC_ERROR_CONNECTION__NO_SERVER, "ipc_connection: no server parameter"}
|
||||
, {IPC_ERROR_CONNECTION__NO_SERVICE_NAME, "ipc_connection: no service name parameter"}
|
||||
, {IPC_ERROR_CONNECTION__NO_ENVIRONMENT_PARAM, "ipc_connection: no environment param"}
|
||||
, {IPC_ERROR_USOCK_CONNECT__CONNECT, "ipc_connection: error on the connect function"}
|
||||
|
||||
, {IPC_ERROR_CONNECTION_GEN__NO_CINFO, "ipc_connection_gen: no cinfo"}
|
||||
|
||||
, {IPC_ERROR_ACCEPT__NO_SERVICE_PARAM, "ipc_accept: no service param"}
|
||||
, {IPC_ERROR_ACCEPT__NO_CLIENT_PARAM , "ipc_accept: no client param"}
|
||||
|
||||
|
@ -36,7 +29,7 @@ static struct ipc_errors_verbose ipc_errors_verbose[] = {
|
|||
, {IPC_ERROR_PROVIDE_FD__SENDMSG , "ipc_provide_fd: sendmsg function"}
|
||||
|
||||
, {IPC_ERROR_WRITE__NO_MESSAGE_PARAM, "ipc_write: no message param"}
|
||||
, {IPC_ERROR_WRITE__NOT_ENOUGH_DATA , "ipc_write: no enough data sent"}
|
||||
, {IPC_ERROR_WRITE_FD__NOT_ENOUGH_DATA , "ipc_write: no enough data sent"}
|
||||
, {IPC_ERROR_READ__NO_MESSAGE_PARAM , "ipc_read: no message param"}
|
||||
|
||||
, {IPC_ERROR_HANDLE_MESSAGE__NOT_ENOUGH_MEMORY , "handle_message: not enough memory"}
|
||||
|
@ -47,8 +40,8 @@ static struct ipc_errors_verbose ipc_errors_verbose[] = {
|
|||
, {IPC_ERROR_WAIT_EVENT__NO_CLIENTS_PARAM, "ipc_wait_event: no clients param"}
|
||||
, {IPC_ERROR_WAIT_EVENT__NO_EVENT_PARAM , "ipc_wait_event: no event param"}
|
||||
|
||||
, {IPC_ERROR_CONTACT_NETWORKD__NO_SERVICE_NAME_PARAM, "ipc_contact_networkd: no service name param"}
|
||||
, {IPC_ERROR_CONTACT_NETWORKD__NO_SERVER_PARAM , "ipc_contact_networkd: no server param"}
|
||||
, {IPC_ERROR_CONTACT_IPCD__NO_SERVICE_NAME_PARAM, "ipc_contact_ipcd: no service name param"}
|
||||
, {IPC_ERROR_CONTACT_IPCD__NO_SERVER_PARAM , "ipc_contact_ipcd: no server param"}
|
||||
|
||||
, {IPC_ERROR_HANDLE_NEW_CONNECTION__MALLOC, "ipc_handle_new_connection: error on malloc function"}
|
||||
|
||||
|
@ -127,6 +120,29 @@ static struct ipc_errors_verbose ipc_errors_verbose[] = {
|
|||
|
||||
, {IPC_ERROR_DIR_SETUP__DIRECTORY_NOT_WRITABLE, "directory_setup_: directory not writable"}
|
||||
, {IPC_ERROR_DIRECTORY_SETUP__PATH_PARAM , "directory_setup_: no path param"}
|
||||
|
||||
, {IPC_ERROR_SERVER_INIT__NOT_ENOUGH_MEMORY, "IPC_ERROR_SERVER_INIT__NOT_ENOUGH_MEMORY"}
|
||||
, {IPC_ERROR_CONNECTION__NOT_ENOUGH_MEMORY, "IPC_ERROR_CONNECTION__NOT_ENOUGH_MEMORY"}
|
||||
, {IPC_ERROR_CTX_INIT__NO_CONTEXT_PARAM, "IPC_ERROR_CTX_INIT__NO_CONTEXT_PARAM"}
|
||||
, {IPC_ERROR_CTX_INIT__CONTEXT_ALREADY_INIT, "IPC_ERROR_CTX_INIT__CONTEXT_ALREADY_INIT"}
|
||||
, {IPC_ERROR_ADD__MALLOC_POLLFD, "IPC_ERROR_ADD__MALLOC_POLLFD"}
|
||||
, {IPC_ERROR_ADD_MESSAGE_TO_SEND__EMPTY_LIST, "IPC_ERROR_ADD_MESSAGE_TO_SEND__EMPTY_LIST"}
|
||||
, {IPC_ERROR_ADD_MESSAGE_TO_SEND__MALLOC, "IPC_ERROR_ADD_MESSAGE_TO_SEND__MALLOC"}
|
||||
, {IPC_ERROR_ADD_MESSAGE_TO_SEND__NO_PARAM_MESSAGE, "IPC_ERROR_ADD_MESSAGE_TO_SEND__NO_PARAM_MESSAGE"}
|
||||
, {IPC_ERROR_ADD_MESSAGE_TO_SEND__NO_PARAM_MESSAGES, "IPC_ERROR_ADD_MESSAGE_TO_SEND__NO_PARAM_MESSAGES"}
|
||||
, {IPC_ERROR_CONNECTION__NO_CTX, "IPC_ERROR_CONNECTION__NO_CTX"}
|
||||
, {IPC_ERROR_CTX_INIT__MALLOC_CTX, "IPC_ERROR_CTX_INIT__MALLOC_CTX"}
|
||||
, {IPC_ERROR_CTX_INIT__MALLOC_POLLFD, "IPC_ERROR_CTX_INIT__MALLOC_POLLFD"}
|
||||
, {IPC_ERROR_CONTACT_IPCD__NO_FD_PARAM, "IPC_ERROR_CONTACT_IPCD__NO_FD_PARAM"}
|
||||
, {IPC_ERROR_HANDLE_NEW_CONNECTION__INCONSISTENT_INDEX, "IPC_ERROR_HANDLE_NEW_CONNECTION__INCONSISTENT_INDEX"}
|
||||
, {IPC_ERROR_DEL_MESSAGE_TO_SEND__NO_PARAM_MESSAGES, "IPC_ERROR_DEL_MESSAGE_TO_SEND__NO_PARAM_MESSAGES"}
|
||||
, {IPC_ERROR_MESSAGE_DEL__INDEX_ERROR, "IPC_ERROR_MESSAGE_DEL__INDEX_ERROR"}
|
||||
, {IPC_ERROR_MESSAGE_DEL__EMPTY_LIST, "IPC_ERROR_MESSAGE_DEL__EMPTY_LIST"}
|
||||
, {IPC_ERROR_ADD__NO_PARAM_POLLFD, "IPC_ERROR_ADD__NO_PARAM_POLLFD"}
|
||||
, {IPC_ERROR_WRITE__FD_NOT_FOUND, "IPC_ERROR_WRITE__FD_NOT_FOUND"}
|
||||
, {IPC_ERROR_FD_SWITCHING__NO_FD_RECORD, "IPC_ERROR_FD_SWITCHING__NO_FD_RECORD"}
|
||||
, {IPC_ERROR_CLOSE_ALL__NO_CTX_PARAM, "IPC_ERROR_CLOSE_ALL__NO_CTX_PARAM" }
|
||||
, {IPC_ERROR_CLOSE__NO_CTX_PARAM, "IPC_ERROR_CLOSE__NO_CTX_PARAM"}
|
||||
};
|
||||
|
||||
const char *ipc_errors_get (enum ipc_error_code e)
|
||||
|
|
260
src/ipc.h
260
src/ipc.h
|
@ -9,6 +9,8 @@
|
|||
#include <errno.h> // error numbers
|
||||
#include <time.h>
|
||||
|
||||
#include <poll.h>
|
||||
|
||||
/***
|
||||
* global defaults
|
||||
**/
|
||||
|
@ -16,6 +18,7 @@
|
|||
#define RUNDIR "/run/ipc/"
|
||||
#define PATH_MAX 4096
|
||||
#define IPC_HEADER_SIZE 6
|
||||
// #define __IPC_BASE_SIZE 500000 // 500 KB
|
||||
#define __IPC_BASE_SIZE 2000000 // 2 MB, plenty enough space for messages
|
||||
#define IPC_MAX_MESSAGE_SIZE __IPC_BASE_SIZE-IPC_HEADER_SIZE
|
||||
|
||||
|
@ -39,7 +42,7 @@ enum msg_types {
|
|||
, MSG_TYPE_ERR = 1
|
||||
, MSG_TYPE_DATA = 2
|
||||
, MSG_TYPE_NETWORK_LOOKUP = 3
|
||||
} message_types;
|
||||
};
|
||||
|
||||
/**
|
||||
* Event types.
|
||||
|
@ -73,13 +76,23 @@ enum msg_types {
|
|||
enum ipc_event_type {
|
||||
IPC_EVENT_TYPE_NOT_SET = 0
|
||||
, IPC_EVENT_TYPE_ERROR = 1
|
||||
, IPC_EVENT_TYPE_EXTRA_SOCKET = 2
|
||||
, IPC_EVENT_TYPE_SWITCH = 3
|
||||
, IPC_EVENT_TYPE_CONNECTION = 4
|
||||
, IPC_EVENT_TYPE_DISCONNECTION = 5
|
||||
, IPC_EVENT_TYPE_MESSAGE = 6
|
||||
, IPC_EVENT_TYPE_LOOKUP = 7
|
||||
, IPC_EVENT_TYPE_TIMER = 8
|
||||
, IPC_EVENT_TYPE_EXTRA_SOCKET = 2 // Message received from a non IPC socket.
|
||||
, IPC_EVENT_TYPE_SWITCH = 3 // Message to send to a corresponding fd.
|
||||
, IPC_EVENT_TYPE_CONNECTION = 4 // New user.
|
||||
, IPC_EVENT_TYPE_DISCONNECTION = 5 // User disconnected.
|
||||
, IPC_EVENT_TYPE_MESSAGE = 6 // New message.
|
||||
, IPC_EVENT_TYPE_LOOKUP = 7 // Client asking for a service through ipcd.
|
||||
, IPC_EVENT_TYPE_TIMER = 8 // Timeout in the poll(2) function.
|
||||
, IPC_EVENT_TYPE_TX = 9 // Message sent.
|
||||
};
|
||||
|
||||
// For IO callbacks (switching).
|
||||
enum ipccb {
|
||||
IPC_CB_NO_ERROR = 0 // No error. A message was generated.
|
||||
, IPC_CB_FD_CLOSING = 1 // The fd is closing.
|
||||
, IPC_CB_FD_ERROR = 2 // Generic error.
|
||||
, IPC_CB_PARSING_ERROR = 3 // The message was read but with errors.
|
||||
, IPC_CB_IGNORE = 4 // The message should be ignored (protocol specific).
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -88,8 +101,6 @@ enum ipc_event_type {
|
|||
*/
|
||||
enum ipc_error_code {
|
||||
IPC_ERROR_NONE = 0
|
||||
, IPC_ERROR_SERVER_INIT__NON_WRITABLE_DIR = 1
|
||||
, IPC_ERROR_SERVER_INIT__NO_DIR_CANNOT_CREATE_IT = 2
|
||||
, IPC_ERROR_HANDLE_MESSAGE__NOT_ENOUGH_MEMORY = 3
|
||||
, IPC_ERROR_CLOSED_RECIPIENT = 4
|
||||
, IPC_ERROR_SERVICE_PATH__NO_PATH = 5
|
||||
|
@ -99,12 +110,10 @@ enum ipc_error_code {
|
|||
, IPC_ERROR_SERVER_INIT__NO_SERVER_NAME_PARAM = 9
|
||||
, IPC_ERROR_SERVER_INIT__MALLOC = 10
|
||||
, IPC_ERROR_WRITE__NO_MESSAGE_PARAM = 11
|
||||
, IPC_ERROR_WRITE__NOT_ENOUGH_DATA = 12
|
||||
, IPC_ERROR_WRITE_FD__NOT_ENOUGH_DATA = 12
|
||||
, IPC_ERROR_READ__NO_MESSAGE_PARAM = 13
|
||||
, IPC_ERROR_CONNECTION__NO_SERVER = 14
|
||||
, IPC_ERROR_CONNECTION__NO_SERVICE_NAME = 15
|
||||
, IPC_ERROR_CONNECTION__NO_ENVIRONMENT_PARAM = 16
|
||||
, IPC_ERROR_CONNECTION_GEN__NO_CINFO = 17
|
||||
, IPC_ERROR_ACCEPT__NO_SERVICE_PARAM = 18
|
||||
, IPC_ERROR_ACCEPT__NO_CLIENT_PARAM = 19
|
||||
, IPC_ERROR_HANDLE_NEW_CONNECTION__NO_CINFO_PARAM = 20
|
||||
|
@ -123,8 +132,8 @@ enum ipc_error_code {
|
|||
, IPC_ERROR_DEL_FD__EMPTIED_LIST = 33
|
||||
, IPC_ERROR_DEL_FD__EMPTY_LIST = 34
|
||||
, IPC_ERROR_DEL_FD__CANNOT_FIND_CLIENT = 35
|
||||
, IPC_ERROR_CONTACT_NETWORKD__NO_SERVICE_NAME_PARAM = 36
|
||||
, IPC_ERROR_CONTACT_NETWORKD__NO_SERVER_PARAM = 37
|
||||
, IPC_ERROR_CONTACT_IPCD__NO_SERVICE_NAME_PARAM = 36
|
||||
, IPC_ERROR_CONTACT_IPCD__NO_SERVER_PARAM = 37
|
||||
, IPC_ERROR_DEL__EMPTY_LIST = 38
|
||||
, IPC_ERROR_DEL__EMPTIED_LIST = 39
|
||||
, IPC_ERROR_DEL__CANNOT_FIND_CLIENT = 40
|
||||
|
@ -173,6 +182,31 @@ enum ipc_error_code {
|
|||
, IPC_ERROR_DIR_SETUP__NOT_A_DIRECTORY = 83
|
||||
, IPC_ERROR_DIR_SETUP__DIRECTORY_NOT_WRITABLE = 84
|
||||
, IPC_ERROR_DIRECTORY_SETUP__PATH_PARAM = 85
|
||||
|
||||
, IPC_ERROR_SERVER_INIT__NOT_ENOUGH_MEMORY = 86
|
||||
, IPC_ERROR_CONNECTION__NOT_ENOUGH_MEMORY = 87
|
||||
, IPC_ERROR_CTX_INIT__NO_CONTEXT_PARAM = 88
|
||||
, IPC_ERROR_CTX_INIT__CONTEXT_ALREADY_INIT = 89
|
||||
, IPC_ERROR_ADD__MALLOC_POLLFD = 90
|
||||
, IPC_ERROR_ADD_MESSAGE_TO_SEND__EMPTY_LIST = 91
|
||||
, IPC_ERROR_ADD_MESSAGE_TO_SEND__MALLOC = 92
|
||||
, IPC_ERROR_ADD_MESSAGE_TO_SEND__NO_PARAM_MESSAGE = 93
|
||||
, IPC_ERROR_ADD_MESSAGE_TO_SEND__NO_PARAM_MESSAGES = 94
|
||||
, IPC_ERROR_CONNECTION__NO_CTX = 95
|
||||
, IPC_ERROR_CTX_INIT__MALLOC_CTX = 96
|
||||
, IPC_ERROR_CTX_INIT__MALLOC_POLLFD = 97
|
||||
, IPC_ERROR_CONTACT_IPCD__NO_FD_PARAM = 98
|
||||
, IPC_ERROR_HANDLE_NEW_CONNECTION__INCONSISTENT_INDEX = 99
|
||||
, IPC_ERROR_DEL_MESSAGE_TO_SEND__NO_PARAM_MESSAGES = 100
|
||||
, IPC_ERROR_MESSAGE_DEL__INDEX_ERROR = 101
|
||||
, IPC_ERROR_MESSAGE_DEL__EMPTY_LIST = 102
|
||||
, IPC_ERROR_ADD__NO_PARAM_POLLFD = 103
|
||||
, IPC_ERROR_WRITE__FD_NOT_FOUND = 104
|
||||
, IPC_ERROR_ADD__NOT_ENOUGH_MEMORY = 105
|
||||
, IPC_ERROR_WAIT_EVENT__POLL = 106
|
||||
, IPC_ERROR_FD_SWITCHING__NO_FD_RECORD = 107
|
||||
, IPC_ERROR_CLOSE_ALL__NO_CTX_PARAM = 108
|
||||
, IPC_ERROR_CLOSE__NO_CTX_PARAM = 109
|
||||
};
|
||||
|
||||
struct ipc_error {
|
||||
|
@ -186,49 +220,105 @@ struct ipc_error {
|
|||
// with the error_message string in the ipc_error structure.
|
||||
const char *ipc_errors_get (enum ipc_error_code e);
|
||||
|
||||
|
||||
enum ipc_connection_type {
|
||||
IPC_CONNECTION_TYPE_IPC = 0
|
||||
, IPC_CONNECTION_TYPE_EXTERNAL = 1
|
||||
/** Messages received = new connections. */
|
||||
, IPC_CONNECTION_TYPE_SERVER = 2
|
||||
/** IO operations should go through registered callbacks. */
|
||||
, IPC_CONNECTION_TYPE_SWITCHED = 3
|
||||
};
|
||||
|
||||
struct ipc_connection_info {
|
||||
uint32_t version;
|
||||
uint32_t index;
|
||||
int32_t fd;
|
||||
char type; // server, client, arbitrary fd
|
||||
enum ipc_connection_type type;
|
||||
short int more_to_read;
|
||||
char *spath; // max size: PATH_MAX
|
||||
};
|
||||
|
||||
struct ipc_connection_infos {
|
||||
struct ipc_connection_info **cinfos;
|
||||
struct ipc_message {
|
||||
char type; // Internal message type.
|
||||
char user_type; // User-defined message type.
|
||||
int fd; // File descriptor concerned about this message.
|
||||
uint32_t length; // Payload length.
|
||||
char *payload;
|
||||
};
|
||||
|
||||
struct ipc_messages {
|
||||
struct ipc_message *messages;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
struct ipc_message {
|
||||
char type;
|
||||
char user_type;
|
||||
uint32_t length;
|
||||
char *payload;
|
||||
struct ipc_switching {
|
||||
int orig;
|
||||
int dest;
|
||||
enum ipccb (*orig_in) (int origin_fd, struct ipc_message *m, short int *more_to_read);
|
||||
enum ipccb (*orig_out) (int origin_fd, struct ipc_message *m);
|
||||
enum ipccb (*dest_in) (int origin_fd, struct ipc_message *m, short int *more_to_read);
|
||||
enum ipccb (*dest_out) (int origin_fd, struct ipc_message *m);
|
||||
};
|
||||
|
||||
struct ipc_switchings {
|
||||
struct ipc_switching *collection;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
void ipc_message_copy (struct ipc_message *m
|
||||
, uint32_t fd
|
||||
, uint8_t type
|
||||
, uint8_t utype
|
||||
, char *payload
|
||||
, uint32_t paylen);
|
||||
struct ipc_error ipc_messages_del (struct ipc_messages *messages, uint32_t index);
|
||||
|
||||
/**
|
||||
* Context of the whole networking state.
|
||||
*/
|
||||
struct ipc_ctx {
|
||||
/**
|
||||
* Keep track of connections.
|
||||
*/
|
||||
struct ipc_connection_info *cinfos;
|
||||
/**
|
||||
* List of "pollfd" structures within cinfos, so we can pass it to poll(2).
|
||||
*/
|
||||
struct pollfd *pollfd;
|
||||
/**
|
||||
* Size of the connection list.
|
||||
*/
|
||||
size_t size;
|
||||
/**
|
||||
* List of messages to send, once the fd are available.
|
||||
*/
|
||||
struct ipc_messages tx;
|
||||
/**
|
||||
* Relations between fd.
|
||||
*/
|
||||
struct ipc_switchings switchdb;
|
||||
};
|
||||
|
||||
struct ipc_event {
|
||||
enum ipc_event_type type;
|
||||
struct ipc_connection_info *origin;
|
||||
void *m; // message pointer
|
||||
uint32_t index;
|
||||
int origin;
|
||||
void *m; // message pointer
|
||||
};
|
||||
|
||||
/***
|
||||
* ipc event macros
|
||||
**/
|
||||
|
||||
#define IPC_EVENT_SET(pevent,type_,message_,origin_) {\
|
||||
#define IPC_EVENT_SET(pevent,type_,index_, origin_fd_,message_) {\
|
||||
pevent->type = type_; \
|
||||
pevent->index = index_; \
|
||||
pevent->origin = origin_fd_; \
|
||||
pevent->m = message_; \
|
||||
pevent->origin = origin_; \
|
||||
};
|
||||
|
||||
enum ipc_connection_types {
|
||||
IPC_CONNECTION_TYPE_IPC = 0
|
||||
, IPC_CONNECTION_TYPE_EXTERNAL = 1
|
||||
};
|
||||
|
||||
#define IPC_EVENT_CLEAN(pevent) {\
|
||||
pevent->type = IPC_EVENT_TYPE_NOT_SET;\
|
||||
pevent->origin = 0;\
|
||||
pevent->index = 0;\
|
||||
if (pevent->m != NULL) {\
|
||||
ipc_message_empty (pevent->m);\
|
||||
free(pevent->m);\
|
||||
|
@ -240,35 +330,33 @@ enum ipc_connection_types {
|
|||
* main public functions
|
||||
**/
|
||||
|
||||
struct ipc_error ipc_server_init (char **env, struct ipc_connection_info *srv, const char *sname);
|
||||
struct ipc_error ipc_connection (char **env, struct ipc_connection_info *srv, const char *sname);
|
||||
struct ipc_error ipc_wait_event (struct ipc_ctx *, struct ipc_event *, int *timer);
|
||||
|
||||
struct ipc_error ipc_server_close (struct ipc_connection_info *srv);
|
||||
struct ipc_error ipc_close (struct ipc_connection_info *p);
|
||||
struct ipc_error ipc_server_init (struct ipc_ctx *ctx, const char *sname);
|
||||
struct ipc_error ipc_connection (struct ipc_ctx *ctx, const char *sname, int *fd);
|
||||
struct ipc_error ipc_connection_switched (struct ipc_ctx *ctx, const char *sname, int clientfd, int *serverfd);
|
||||
|
||||
struct ipc_error ipc_read (const struct ipc_connection_info *, struct ipc_message *m);
|
||||
struct ipc_error ipc_write (const struct ipc_connection_info *, const struct ipc_message *m);
|
||||
struct ipc_error ipc_close (struct ipc_ctx *ctx, uint32_t index);
|
||||
struct ipc_error ipc_close_all (struct ipc_ctx *ctx);
|
||||
|
||||
struct ipc_error ipc_wait_event (struct ipc_connection_infos *clients
|
||||
, struct ipc_connection_info *srv
|
||||
, struct ipc_event *event, double *timer);
|
||||
void ipc_ctx_free (struct ipc_ctx *ctx);
|
||||
|
||||
struct ipc_error ipc_read (const struct ipc_ctx *, uint32_t index, struct ipc_message *m);
|
||||
struct ipc_error ipc_read_fd (int32_t fd, struct ipc_message *m);
|
||||
struct ipc_error ipc_write (struct ipc_ctx *, const struct ipc_message *m);
|
||||
|
||||
struct ipc_error fd_switching_read (struct ipc_event *event, struct ipc_ctx *ctx, int index);
|
||||
struct ipc_error fd_switching_write (struct ipc_event *event, struct ipc_ctx *ctx, int index);
|
||||
|
||||
// store and remove only pointers on allocated structures
|
||||
struct ipc_error ipc_add (struct ipc_connection_infos *, struct ipc_connection_info *);
|
||||
struct ipc_error ipc_del (struct ipc_connection_infos *, struct ipc_connection_info *);
|
||||
struct ipc_error ipc_add (struct ipc_ctx *, struct ipc_connection_info *, struct pollfd *);
|
||||
struct ipc_error ipc_del (struct ipc_ctx *, uint32_t index);
|
||||
|
||||
// add an arbitrary file descriptor to read
|
||||
struct ipc_error ipc_add_fd (struct ipc_connection_infos *cinfos, int fd);
|
||||
struct ipc_error ipc_del_fd (struct ipc_connection_infos *cinfos, int fd);
|
||||
|
||||
void ipc_connections_free (struct ipc_connection_infos *);
|
||||
|
||||
// create the client service structure
|
||||
struct ipc_error ipc_connection_gen (struct ipc_connection_info *cinfo
|
||||
, uint32_t index, uint32_t version
|
||||
, int fd, char type);
|
||||
|
||||
void ipc_connections_close (struct ipc_connection_infos *cinfos);
|
||||
struct ipc_error ipc_add_fd (struct ipc_ctx *ctx, int fd);
|
||||
// add a switched file descriptor to read
|
||||
struct ipc_error ipc_add_fd_switched (struct ipc_ctx *ctx, int fd);
|
||||
struct ipc_error ipc_del_fd (struct ipc_ctx *ctx, int fd);
|
||||
|
||||
/***
|
||||
* message functions
|
||||
|
@ -286,50 +374,49 @@ struct ipc_error ipc_message_format_data (struct ipc_message *m, char utype, con
|
|||
struct ipc_error ipc_message_format_server_close (struct ipc_message *m);
|
||||
struct ipc_error ipc_message_empty (struct ipc_message *m);
|
||||
|
||||
struct ipc_error ipc_messages_add (struct ipc_messages *, const struct ipc_message *);
|
||||
void ipc_messages_free (struct ipc_messages *);
|
||||
|
||||
// Switch cases macros
|
||||
// print on error
|
||||
#define ERROR_CASE(e,f,m) case e : { fprintf (stderr, "function %s: %s", f, m); } break;
|
||||
#define ERROR_CASE(e,f,m) case e : { fprintf (stderr, "function %s: %s\n", f, m); } break;
|
||||
|
||||
/***
|
||||
* non public functions
|
||||
**/
|
||||
|
||||
void ipc_connection_print (struct ipc_connection_info *cinfo);
|
||||
void ipc_connections_print (struct ipc_connection_infos *cinfos);
|
||||
struct ipc_error ipc_write_fd (int fd, const struct ipc_message *m);
|
||||
struct ipc_error ipc_ctx_init (struct ipc_ctx **);
|
||||
struct ipc_error ipc_ctx_new_alloc (struct ipc_ctx *ctx);
|
||||
struct ipc_error service_path (char *path, const char *sname);
|
||||
struct ipc_error handle_writing_message (struct ipc_event *event, struct ipc_ctx *ctx, uint32_t index);
|
||||
|
||||
struct ipc_error ipc_accept (struct ipc_connection_info *srv, struct ipc_connection_info *p);
|
||||
struct ipc_error ipc_contact_networkd (struct ipc_connection_info *srv, const char *sname);
|
||||
struct ipc_error service_path (char *path, const char *sname, int32_t index, int32_t version);
|
||||
void ipc_ctx_print (struct ipc_ctx *ctx);
|
||||
|
||||
// Last parameter is the index for the server fd in the context structure.
|
||||
struct ipc_error ipc_accept_add (struct ipc_event *event, struct ipc_ctx *ctx, uint32_t index);
|
||||
struct ipc_error ipc_contact_ipcd (int *pfd, const char *sname);
|
||||
struct ipc_error service_path (char *path, const char *sname);
|
||||
|
||||
/***
|
||||
* networkd enumerations, structures and functions
|
||||
* ipcd enumerations, structures and functions
|
||||
**/
|
||||
|
||||
struct ipc_switching {
|
||||
int orig;
|
||||
int dest;
|
||||
};
|
||||
|
||||
struct ipc_switchings {
|
||||
struct ipc_switching *collection;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
struct networkd {
|
||||
int cpt;
|
||||
struct ipc_connection_info *srv;
|
||||
struct ipc_connection_infos *clients;
|
||||
struct ipc_switchings *TCP_TO_IPC;
|
||||
};
|
||||
|
||||
struct ipc_error ipc_wait_event_networkd (struct ipc_connection_infos *cinfos
|
||||
, struct ipc_connection_info *cinfo // cinfo is NULL for clients
|
||||
, struct ipc_event *event, struct ipc_switchings *switchdb, double *timer);
|
||||
|
||||
void ipc_ctx_switching_add (struct ipc_ctx *ctx, int orig, int dest);
|
||||
int ipc_ctx_switching_del (struct ipc_ctx *ctx, int fd);
|
||||
void ipc_switching_add (struct ipc_switchings *is, int orig, int dest);
|
||||
int ipc_switching_del (struct ipc_switchings *is, int fd);
|
||||
int ipc_switching_get (struct ipc_switchings *is, int fd);
|
||||
void ipc_switching_free (struct ipc_switchings *is);
|
||||
void ipc_switching_callbacks_ (struct ipc_ctx *ctx, int fd
|
||||
, enum ipccb (*cb_in )(int fd, struct ipc_message *m, short int *more_to_read));
|
||||
void ipc_switching_callbacks (
|
||||
struct ipc_ctx *ctx
|
||||
, int fd
|
||||
, enum ipccb (*cb_in )(int fd, struct ipc_message *m, short int *more_to_read)
|
||||
, enum ipccb (*cb_out)(int fd, struct ipc_message *m));
|
||||
|
||||
int ipc_ctx_fd_type (struct ipc_ctx *ctx, int fd, enum ipc_connection_type type);
|
||||
|
||||
void ipc_switching_print (struct ipc_switchings *is);
|
||||
|
||||
|
@ -439,6 +526,7 @@ struct ipc_error ipc_provide_fd (int sock, int fd);
|
|||
TEST_IPC_RR_F(function_to_test, "%s", err_message)
|
||||
|
||||
// same as TEST_IPC_RR but do not return
|
||||
// Also: print the error right away.
|
||||
#define TEST_IPC_P(function_to_test, err_message) {\
|
||||
struct ipc_error ret = function_to_test;\
|
||||
if (ret.error_code != IPC_ERROR_NONE) {\
|
||||
|
@ -446,6 +534,8 @@ struct ipc_error ipc_provide_fd (int sock, int fd);
|
|||
, "non blocking error" \
|
||||
, ":" __FILE__ "%s:%s" \
|
||||
, err_message );\
|
||||
\
|
||||
fprintf (stderr, "%s\n", ret.error_message);\
|
||||
}\
|
||||
}
|
||||
|
||||
|
|
|
@ -145,3 +145,87 @@ struct ipc_error ipc_message_empty (struct ipc_message *m)
|
|||
|
||||
IPC_RETURN_NO_ERROR;
|
||||
}
|
||||
|
||||
// store and remove only pointers on allocated structures
|
||||
struct ipc_error ipc_messages_add (struct ipc_messages *messages, const struct ipc_message *message)
|
||||
{
|
||||
T_R ((messages == NULL), IPC_ERROR_ADD_MESSAGE_TO_SEND__NO_PARAM_MESSAGES);
|
||||
T_R ((message == NULL), IPC_ERROR_ADD_MESSAGE_TO_SEND__NO_PARAM_MESSAGE);
|
||||
|
||||
messages->size++;
|
||||
if (messages->size == 1 && messages->messages == NULL) {
|
||||
// first allocation
|
||||
SECURE_BUFFER_HEAP_ALLOCATION_R (messages->messages, sizeof (struct ipc_message),,
|
||||
IPC_ERROR_ADD_MESSAGE_TO_SEND__MALLOC);
|
||||
} else {
|
||||
messages->messages = realloc (messages->messages, sizeof (struct ipc_message) * messages->size);
|
||||
}
|
||||
|
||||
T_R ((messages->messages == NULL), IPC_ERROR_ADD_MESSAGE_TO_SEND__EMPTY_LIST);
|
||||
|
||||
// DEEP COPY.
|
||||
messages->messages[messages->size -1] = *message;
|
||||
if (message->length > 0 && message->payload != NULL) {
|
||||
messages->messages[messages->size -1].payload = malloc(message->length * sizeof (char));
|
||||
strncpy(messages->messages[messages->size -1].payload, message->payload, message->length);
|
||||
}
|
||||
else {
|
||||
messages->messages[messages->size -1].payload = NULL;
|
||||
}
|
||||
|
||||
IPC_RETURN_NO_ERROR;
|
||||
}
|
||||
|
||||
// Remove only pointers on allocated structures.
|
||||
struct ipc_error ipc_messages_del (struct ipc_messages *messages, uint32_t index)
|
||||
{
|
||||
T_R ((messages == NULL), IPC_ERROR_DEL_MESSAGE_TO_SEND__NO_PARAM_MESSAGES);
|
||||
T_R ((messages->size == 0 || index >= messages->size), IPC_ERROR_MESSAGE_DEL__INDEX_ERROR);
|
||||
|
||||
// NOT A DEEP COPY.
|
||||
messages->size--;
|
||||
if (messages->size == 0) {
|
||||
free (messages->messages);
|
||||
messages->messages = NULL;
|
||||
}
|
||||
else {
|
||||
messages->messages[index] = messages->messages[messages->size];
|
||||
messages->messages = realloc (messages->messages, sizeof (struct ipc_message) * messages->size);
|
||||
T_R ((messages->messages == NULL), IPC_ERROR_MESSAGE_DEL__EMPTY_LIST);
|
||||
}
|
||||
|
||||
IPC_RETURN_NO_ERROR;
|
||||
}
|
||||
|
||||
void ipc_message_copy (struct ipc_message *m
|
||||
, uint32_t fd
|
||||
, uint8_t type
|
||||
, uint8_t utype
|
||||
, char *payload
|
||||
, uint32_t paylen)
|
||||
{
|
||||
// printf("starting the message copy\n");
|
||||
m->fd = fd;
|
||||
m->type = type;
|
||||
m->user_type = utype;
|
||||
m->length = paylen;
|
||||
if (m->payload != NULL) {
|
||||
free(m->payload);
|
||||
}
|
||||
// printf("BEFORE THE PAYLOAD COPY\n");
|
||||
m->payload = malloc(sizeof(char) * paylen);
|
||||
memcpy(m->payload, payload, paylen);
|
||||
// printf("PAYLOAD COPY DONE\n");
|
||||
}
|
||||
|
||||
void ipc_messages_free (struct ipc_messages *messages)
|
||||
{
|
||||
if (messages != NULL)
|
||||
{
|
||||
if (messages->messages != NULL)
|
||||
{
|
||||
free(messages->messages);
|
||||
messages->messages = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
338
src/network.c
338
src/network.c
|
@ -20,12 +20,12 @@
|
|||
|
||||
/**
|
||||
* TODO:
|
||||
* describe a protocol to get this working into networkd
|
||||
* asking networkd for a fd with an URI
|
||||
* describe a protocol to get this working into ipcd
|
||||
* asking ipcd for a fd with an URI
|
||||
* URI should contain: who (the service name), where (destination), how (protocol)
|
||||
* networkd initiates a communication with the requested service
|
||||
* networkd sends the fd
|
||||
* get a networkd working with this
|
||||
* ipcd initiates a communication with the requested service
|
||||
* ipcd sends the fd
|
||||
* get a ipcd working with this
|
||||
*/
|
||||
|
||||
struct ipc_error ipc_receive_fd (int sock, int *fd)
|
||||
|
@ -81,9 +81,21 @@ struct ipc_error ipc_provide_fd (int sock, int fd)
|
|||
IPC_RETURN_NO_ERROR;
|
||||
}
|
||||
|
||||
void ipc_ctx_switching_add (struct ipc_ctx *ctx, int orig, int dest)
|
||||
{
|
||||
ipc_switching_add (&ctx->switchdb, orig, dest);
|
||||
}
|
||||
|
||||
void ipc_switching_add (struct ipc_switchings *is, int orig, int dest)
|
||||
{
|
||||
is->collection = realloc (is->collection, sizeof (struct ipc_switching) * (is->size + 1));
|
||||
if (is->collection == NULL) {
|
||||
is->collection = malloc (sizeof (struct ipc_switching) * (is->size + 1));
|
||||
}
|
||||
else {
|
||||
is->collection = realloc (is->collection, sizeof (struct ipc_switching) * (is->size + 1));
|
||||
}
|
||||
|
||||
/** TODO: less brutal approach */
|
||||
if (is->collection == NULL) {
|
||||
fprintf (stderr, __FILE__ " error realloc line %d", __LINE__);
|
||||
exit (EXIT_FAILURE);
|
||||
|
@ -93,6 +105,16 @@ void ipc_switching_add (struct ipc_switchings *is, int orig, int dest)
|
|||
|
||||
is->collection[is->size - 1].orig = orig;
|
||||
is->collection[is->size - 1].dest = dest;
|
||||
|
||||
is->collection[is->size - 1].orig_in = NULL;
|
||||
is->collection[is->size - 1].dest_in = NULL;
|
||||
is->collection[is->size - 1].orig_out = NULL;
|
||||
is->collection[is->size - 1].dest_out = NULL;
|
||||
}
|
||||
|
||||
int ipc_ctx_switching_del (struct ipc_ctx *ctx, int fd)
|
||||
{
|
||||
return ipc_switching_del (&ctx->switchdb, fd);
|
||||
}
|
||||
|
||||
int ipc_switching_del (struct ipc_switchings *is, int fd)
|
||||
|
@ -110,13 +132,12 @@ int ipc_switching_del (struct ipc_switchings *is, int fd)
|
|||
is->collection[i].orig = is->collection[is->size - 1].orig;
|
||||
is->collection[i].dest = is->collection[is->size - 1].dest;
|
||||
|
||||
size_t s = (is->size - 1) > 0 ? (is->size - 1) : 1;
|
||||
|
||||
is->collection = realloc (is->collection, sizeof (struct ipc_switching) * s);
|
||||
if (is->collection == NULL) {
|
||||
/** TODO: not sure we want this behavior */
|
||||
fprintf (stderr, __FILE__ " error realloc line %d", __LINE__);
|
||||
exit (EXIT_FAILURE);
|
||||
if (is->size == 1) {
|
||||
free(is->collection);
|
||||
is->collection = NULL;
|
||||
}
|
||||
else {
|
||||
is->collection = realloc (is->collection, sizeof (struct ipc_switching) * (is->size-1));
|
||||
}
|
||||
|
||||
is->size--;
|
||||
|
@ -127,6 +148,28 @@ int ipc_switching_del (struct ipc_switchings *is, int fd)
|
|||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 0 = fd is origin
|
||||
* 1 = fd is dest
|
||||
* -1 = not found
|
||||
*/
|
||||
int ipc_switching_get_ (const struct ipc_switchings *is
|
||||
, int fd
|
||||
, struct ipc_switching **s)
|
||||
{
|
||||
for (size_t i = 0; i < is->size; i++) {
|
||||
if (is->collection[i].orig == fd) {
|
||||
*s = &is->collection[i];
|
||||
return 0;
|
||||
} else if (is->collection[i].dest == fd) {
|
||||
*s = &is->collection[i];
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ipc_switching_get (struct ipc_switchings *is, int fd)
|
||||
{
|
||||
for (size_t i = 0; i < is->size; i++) {
|
||||
|
@ -151,3 +194,272 @@ void ipc_switching_free (struct ipc_switchings *is)
|
|||
}
|
||||
is->size = 0;
|
||||
}
|
||||
|
||||
enum ipccb
|
||||
default_cb_in(int fd, struct ipc_message *m, short int *more_to_read)
|
||||
{
|
||||
*more_to_read = 0;
|
||||
|
||||
size_t msize = IPC_MAX_MESSAGE_SIZE;
|
||||
SECURE_BUFFER_DECLARATION (char, buf, msize);
|
||||
char *pbuf = buf;
|
||||
|
||||
// By default, usock_read (a wrapper around read(2)) is used.
|
||||
|
||||
{ /** Some macros use "ret" as a variable name, so this is to be sure. */
|
||||
struct ipc_error ret = usock_recv (fd, &pbuf, &msize);
|
||||
if (ret.error_code != IPC_ERROR_NONE) {
|
||||
if (ret.error_code == IPC_ERROR_CLOSED_RECIPIENT) {
|
||||
return IPC_CB_FD_CLOSING;
|
||||
}
|
||||
return IPC_CB_FD_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/** There is a message, send it to the corresponding fd **/
|
||||
if (msize > 0) {
|
||||
struct ipc_error ret = ipc_message_format_read (m, buf, msize);
|
||||
if (ret.error_code != IPC_ERROR_NONE) {
|
||||
return IPC_CB_PARSING_ERROR;
|
||||
}
|
||||
return IPC_CB_NO_ERROR;
|
||||
}
|
||||
|
||||
// By default, if msize <= 0 the fd should be closed.
|
||||
return IPC_CB_FD_CLOSING;
|
||||
}
|
||||
|
||||
enum ipccb
|
||||
default_cb_out(int fd, struct ipc_message *m)
|
||||
{
|
||||
size_t msize = 0;
|
||||
SECURE_DECLARATION (struct ipc_error, ret);
|
||||
SECURE_BUFFER_DECLARATION (char, buf, IPC_MAX_MESSAGE_SIZE);
|
||||
char *pbuf = buf;
|
||||
|
||||
ipc_message_format_write (m, &pbuf, &msize);
|
||||
|
||||
size_t nbytes_sent = 0;
|
||||
ret = usock_send (fd, buf, msize, &nbytes_sent);
|
||||
|
||||
// On error or if what was sent != what should have been sent.
|
||||
if (ret.error_code != IPC_ERROR_NONE || nbytes_sent != msize) {
|
||||
return IPC_CB_FD_ERROR;
|
||||
}
|
||||
|
||||
return IPC_CB_NO_ERROR;
|
||||
}
|
||||
|
||||
void ipc_switching_callbacks_ (struct ipc_ctx *ctx, int fd
|
||||
, enum ipccb (*cb_in )(int fd, struct ipc_message *m, short int *more_to_read))
|
||||
{
|
||||
ipc_switching_callbacks (ctx, fd, cb_in, NULL);
|
||||
}
|
||||
|
||||
void ipc_switching_callbacks (
|
||||
struct ipc_ctx *ctx
|
||||
, int fd
|
||||
, enum ipccb (*cb_in )(int fd, struct ipc_message *m, short int *more_to_read)
|
||||
, enum ipccb (*cb_out)(int fd, struct ipc_message *m))
|
||||
{
|
||||
struct ipc_switching *sw = NULL;
|
||||
int is_valid = ipc_switching_get_ (&ctx->switchdb, fd, &sw);
|
||||
if (is_valid == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sw->orig == fd) {
|
||||
sw->orig_in = cb_in;
|
||||
sw->orig_out = cb_out;
|
||||
}
|
||||
else {
|
||||
sw->dest_in = cb_in;
|
||||
sw->dest_out = cb_out;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* fd_switching_read allows to read a message from a switched fd.
|
||||
*/
|
||||
struct ipc_error fd_switching_read (struct ipc_event *event, struct ipc_ctx *ctx, int index)
|
||||
{
|
||||
// printf ("fd_switching_read\n");
|
||||
|
||||
// If the socket is associated to another one for ipcd:
|
||||
// read and write automatically and provide a new IPC_EVENT_TYPE indicating the switch.
|
||||
|
||||
T_R ((ctx->switchdb.size == 0), IPC_ERROR_FD_SWITCHING__NO_FD_RECORD);
|
||||
|
||||
int talkingfd = ctx->pollfd[index].fd;
|
||||
int dest_fd = -1;
|
||||
struct ipc_switching *sw = NULL;
|
||||
struct ipc_message m;
|
||||
memset(&m, 0, sizeof (struct ipc_message));
|
||||
|
||||
enum ipccb r;
|
||||
int is_valid = 0;
|
||||
short int more_to_read = 0;
|
||||
|
||||
is_valid = ipc_switching_get_ (&ctx->switchdb, talkingfd, &sw);
|
||||
|
||||
T_R ((is_valid == -1), IPC_ERROR_FD_SWITCHING__NO_FD_RECORD);
|
||||
|
||||
if (sw->orig == talkingfd) {
|
||||
dest_fd = sw->dest;
|
||||
if (sw->orig_in == NULL) {
|
||||
r = default_cb_in (talkingfd, &m, &more_to_read);
|
||||
}
|
||||
else {
|
||||
r = (*sw->orig_in)(talkingfd, &m, &more_to_read);
|
||||
}
|
||||
}
|
||||
else {
|
||||
dest_fd = sw->orig;
|
||||
if (sw->dest_in == NULL) {
|
||||
r = default_cb_in (talkingfd, &m, &more_to_read);
|
||||
}
|
||||
else {
|
||||
r = (*sw->dest_in)(talkingfd, &m, &more_to_read);
|
||||
}
|
||||
}
|
||||
|
||||
ctx->cinfos[index].more_to_read = more_to_read;
|
||||
|
||||
// Message reception OK: reading the message and put it in the list of messages to send.
|
||||
if (r == IPC_CB_NO_ERROR) {
|
||||
// In case of message reception:
|
||||
// 1. put the message in the list to be sent
|
||||
m.fd = dest_fd;
|
||||
ipc_write (ctx, &m);
|
||||
// 2. delete the message (a deep copy has been made)
|
||||
ipc_message_empty (&m);
|
||||
// 3. set event IPC_EVENT_TYPE_SWITCH, inform ipcd of a successful reception.
|
||||
IPC_EVENT_SET (event, IPC_EVENT_TYPE_SWITCH, index, ctx->pollfd[index].fd, NULL);
|
||||
// 4. IPC_RETURN_NO_ERROR
|
||||
IPC_RETURN_NO_ERROR;
|
||||
}
|
||||
|
||||
// Message reception OK: no message to transfer.
|
||||
// This is applied to protocol-specific messages, for example when the client
|
||||
// has to communicate with the proxy, not the service.
|
||||
if (r == IPC_CB_IGNORE) {
|
||||
// printf ("IGNORING REQUEST\n");
|
||||
// In case of message reception:
|
||||
// 1. set event IPC_EVENT_TYPE_SWITCH, inform ipcd of a successful reception.
|
||||
IPC_EVENT_SET (event, IPC_EVENT_TYPE_SWITCH, index, ctx->pollfd[index].fd, NULL);
|
||||
// 2. IPC_RETURN_NO_ERROR
|
||||
IPC_RETURN_NO_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* NOTE: In any other case, the fd is, or should be closed.
|
||||
*/
|
||||
|
||||
// 1. remove both fd from switchdb
|
||||
// Client and servers should be closed by the libipc user application.
|
||||
// close (sw->dest);
|
||||
// close (talkingfd);
|
||||
|
||||
ipc_del_fd (ctx, sw->dest);
|
||||
ipc_del_fd (ctx, talkingfd);
|
||||
|
||||
ipc_switching_del (&ctx->switchdb, talkingfd);
|
||||
|
||||
// 2. set event (either error or disconnection)
|
||||
if (r == IPC_CB_FD_CLOSING) {
|
||||
IPC_EVENT_SET (event, IPC_EVENT_TYPE_DISCONNECTION, index, talkingfd, NULL);
|
||||
}
|
||||
else {
|
||||
IPC_EVENT_SET (event, IPC_EVENT_TYPE_ERROR, index, talkingfd, NULL);
|
||||
}
|
||||
|
||||
// 3. return IPC_ERROR_CLOSED_RECIPIENT
|
||||
IPC_RETURN_ERROR (IPC_ERROR_CLOSED_RECIPIENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* fd_switching_write allows to read a message from a switched fd.
|
||||
*/
|
||||
struct ipc_error fd_switching_write (struct ipc_event *event, struct ipc_ctx *ctx, int index)
|
||||
{
|
||||
// If the socket is associated to another one for ipcd:
|
||||
// read and write automatically and provide a new IPC_EVENT_TYPE indicating the switch.
|
||||
T_R ((ctx->switchdb.size == 0), IPC_ERROR_FD_SWITCHING__NO_FD_RECORD);
|
||||
|
||||
int output_fd = ctx->pollfd[index].fd;
|
||||
struct ipc_switching *sw = NULL;
|
||||
struct ipc_message *m = NULL;
|
||||
size_t i;
|
||||
|
||||
// search for the next message to send for output_fd fd.
|
||||
for (i = 0; ctx->tx.size ; i++) {
|
||||
if (ctx->tx.messages[i].fd == output_fd) {
|
||||
m = &ctx->tx.messages[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// In case there is no message for the fd: the error will be catched.
|
||||
|
||||
enum ipccb r;
|
||||
int is_valid = 0;
|
||||
|
||||
is_valid = ipc_switching_get_ (&ctx->switchdb, output_fd, &sw);
|
||||
|
||||
T_R ((is_valid == -1), IPC_ERROR_FD_SWITCHING__NO_FD_RECORD);
|
||||
|
||||
if (sw->orig == output_fd) {
|
||||
if (sw->orig_in == NULL) {
|
||||
r = default_cb_out (output_fd, m);
|
||||
}
|
||||
else {
|
||||
r = (*sw->orig_out)(output_fd, m);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (sw->dest_in == NULL) {
|
||||
r = default_cb_out (output_fd, m);
|
||||
}
|
||||
else {
|
||||
r = (*sw->dest_out)(output_fd, m);
|
||||
}
|
||||
}
|
||||
|
||||
// Whether or not the message has been sent, it should be removed.
|
||||
// Freeing the message structure.
|
||||
ipc_message_empty (m);
|
||||
// Removing the message from the context.
|
||||
ipc_messages_del (&ctx->tx, i); // remove the message indexed by i
|
||||
|
||||
// Message reception OK: reading the message and put it in the list of messages to send.
|
||||
if (r == IPC_CB_NO_ERROR) {
|
||||
// 1. set event IPC_EVENT_TYPE_SWITCH, inform ipcd of a successful reception.
|
||||
IPC_EVENT_SET (event, IPC_EVENT_TYPE_TX, index, output_fd, NULL);
|
||||
// 2. IPC_RETURN_NO_ERROR
|
||||
IPC_RETURN_NO_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* NOTE: In any other case, the fd is, or should be closed.
|
||||
*/
|
||||
|
||||
// 1. close and remove both fd from switchdb
|
||||
int delfd = ipc_switching_del (&ctx->switchdb, output_fd);
|
||||
if (delfd >= 0) {
|
||||
close (delfd);
|
||||
ipc_del_fd (ctx, delfd);
|
||||
}
|
||||
close (output_fd);
|
||||
ipc_del_fd (ctx, output_fd);
|
||||
|
||||
// 2. set event (either error or disconnection)
|
||||
if (r == IPC_CB_FD_CLOSING) {
|
||||
IPC_EVENT_SET (event, IPC_EVENT_TYPE_DISCONNECTION, index, output_fd, NULL);
|
||||
}
|
||||
else {
|
||||
IPC_EVENT_SET (event, IPC_EVENT_TYPE_ERROR, index, output_fd, NULL);
|
||||
}
|
||||
|
||||
// 3. return IPC_ERROR_CLOSED_RECIPIENT
|
||||
IPC_RETURN_ERROR (IPC_ERROR_CLOSED_RECIPIENT);
|
||||
}
|
||||
|
|
55
src/print.c
55
src/print.c
|
@ -1,20 +1,51 @@
|
|||
#include "ipc.h"
|
||||
|
||||
void ipc_connection_print (struct ipc_connection_info *cinfo)
|
||||
void ipc_ctx_print (struct ipc_ctx *ctx)
|
||||
{
|
||||
T_R_NOTHING ((cinfo == NULL));
|
||||
printf ("Context contains:\n");
|
||||
for (size_t i = 0; i < ctx->size; i++) {
|
||||
printf ("- fd %d\t", ctx->pollfd[i].fd);
|
||||
|
||||
#if 0
|
||||
LOG_DEBUG ("fd %d: index %d, version %d, type %c, path %s"
|
||||
, cinfo->fd , cinfo->index, cinfo->version, cinfo->type
|
||||
, (cinfo->spath == NULL) ? "-" : cinfo->spath);
|
||||
#endif
|
||||
}
|
||||
switch (ctx->cinfos[i].type) {
|
||||
case IPC_CONNECTION_TYPE_IPC: {
|
||||
printf ("- ipc\n");
|
||||
break;
|
||||
}
|
||||
case IPC_CONNECTION_TYPE_EXTERNAL: {
|
||||
printf ("- external\n");
|
||||
break;
|
||||
}
|
||||
case IPC_CONNECTION_TYPE_SERVER: {
|
||||
printf ("- external\n");
|
||||
break;
|
||||
}
|
||||
case IPC_CONNECTION_TYPE_SWITCHED: {
|
||||
printf ("- switched\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ipc_connections_print (struct ipc_connection_infos *cinfos)
|
||||
{
|
||||
for (size_t i = 0; i < cinfos->size; i++) {
|
||||
ipc_connection_print (cinfos->cinfos[i]);
|
||||
if (ctx->switchdb.size > 0) {
|
||||
printf ("Context.switchdb contains:\n");
|
||||
for (size_t i = 0; i < ctx->switchdb.size; i++) {
|
||||
printf ("- %d <-> %d\n"
|
||||
, ctx->switchdb.collection[i].orig
|
||||
, ctx->switchdb.collection[i].dest);
|
||||
}
|
||||
}
|
||||
else {
|
||||
printf ("Context.switchdb is empty\n");
|
||||
}
|
||||
|
||||
if (ctx->tx.size > 0) {
|
||||
printf ("Context.tx contains:\n");
|
||||
for (size_t i = 0; i < ctx->tx.size; i++) {
|
||||
printf ("- message to %d\n", ctx->tx.messages[i].fd);
|
||||
}
|
||||
}
|
||||
else {
|
||||
printf ("Context.tx is empty\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
#include "ipc.h"
|
||||
|
||||
struct ipc_error
|
||||
service_path (char *path, const char *sname)
|
||||
{
|
||||
T_R ((path == NULL), IPC_ERROR_SERVICE_PATH__NO_PATH);
|
||||
T_R ((sname == NULL), IPC_ERROR_SERVICE_PATH__NO_SERVICE_NAME);
|
||||
|
||||
memset (path, 0, PATH_MAX);
|
||||
|
||||
char *rundir = getenv ("IPC_RUNDIR");
|
||||
if (rundir == NULL)
|
||||
rundir = RUNDIR;
|
||||
|
||||
snprintf (path, PATH_MAX - 1, "%s/%s", rundir, sname);
|
||||
|
||||
IPC_RETURN_NO_ERROR;
|
||||
}
|
||||
|
136
src/usocket.c
136
src/usocket.c
|
@ -23,8 +23,73 @@
|
|||
struct ipc_error usock_send (const int32_t fd, const char *buf, size_t len, size_t * sent)
|
||||
{
|
||||
ssize_t ret = 0;
|
||||
|
||||
#ifdef __PRINT_MSG_SIZES
|
||||
fprintf (stderr, "a %10lu-byte message should be sent to %d\n", len, fd);
|
||||
#endif
|
||||
|
||||
ret = send (fd, buf, len, MSG_NOSIGNAL);
|
||||
T_R ((ret <= 0), IPC_ERROR_USOCK_SEND);
|
||||
if (ret == -1)
|
||||
{
|
||||
// Some choice could be made.
|
||||
switch (errno) {
|
||||
|
||||
// The receive buffer pointer(s) point outside the process's address space.
|
||||
ERROR_CASE (EACCES, "usock_send", "write permission is denied");
|
||||
|
||||
// The socket is marked nonblocking and the requested operation would block.
|
||||
// POSIX.1-2001 allows either error to be returned for this case, and does not
|
||||
// require these constants to have the same value, so a portable application
|
||||
// should check for both possibilities.
|
||||
ERROR_CASE (EWOULDBLOCK, "usock_send", "socket marked as nonblocking, but requested operation would block");
|
||||
|
||||
// ERROR_CASE (EAGAIN, "usock_send", "socket not previously bound to an address and all ports are in use");
|
||||
|
||||
ERROR_CASE (EALREADY, "usock_send", "another Fast Open is in progress");
|
||||
|
||||
ERROR_CASE (EBADF, "usock_send", "sockfd is not a valid open file descriptor");
|
||||
|
||||
ERROR_CASE (ECONNRESET, "usock_send", "Connection reset by peer.");
|
||||
|
||||
ERROR_CASE (EDESTADDRREQ, "usock_send", "socket not connection-mode, and no peer address is set.");
|
||||
|
||||
ERROR_CASE (EFAULT, "usock_send", "an invalid user space address was specified for an argument");
|
||||
|
||||
// See signal(7).
|
||||
ERROR_CASE (EINTR, "usock_send", "a signal occurred before any data was transmitted");
|
||||
|
||||
ERROR_CASE (EINVAL, "usock_send", "invalid argument passed");
|
||||
|
||||
// This error should not happen, and the recipient specification may be ignored.
|
||||
ERROR_CASE (EISCONN, "usock_send", "connection-mode socket was already connected but a recipient was specified");
|
||||
|
||||
// The socket type requires that message be sent atomically, and the size of the message to be sent made this impossible.
|
||||
ERROR_CASE (EMSGSIZE, "usock_send", "cannot send a message of that size");
|
||||
|
||||
// This generally indicates that the interface has stopped sending, but
|
||||
// may be caused by transient congestion. (Normally, this does not occur in Linux.
|
||||
// Packets are just silently dropped when a device queue overflows.)
|
||||
ERROR_CASE (ENOBUFS, "usock_send", "the output queue for the network interface was full");
|
||||
|
||||
ERROR_CASE (ENOMEM, "usock_send", "no memory available");
|
||||
|
||||
ERROR_CASE (ENOTCONN, "usock_send", "the socket is not connected, and no target has been given");
|
||||
|
||||
// Should not happen in libipc (watch out for libipc user application).
|
||||
ERROR_CASE (ENOTSOCK, "usock_send", "the file descriptor sockfd does not refer to a socket");
|
||||
|
||||
// Should not happen in libipc.
|
||||
ERROR_CASE (EOPNOTSUPP, "usock_send", "some bit in the flags argument is inappropriate for the socket type");
|
||||
|
||||
// In this case, the process will also receive a SIGPIPE unless MSG_NOSIGNAL is set.
|
||||
ERROR_CASE (EPIPE, "usock_send", "the local end has been shut down on a connection oriented socket");
|
||||
|
||||
default:
|
||||
fprintf (stderr, "usock_send: unrecognized error after send(2), num: %d\n", errno);
|
||||
}
|
||||
}
|
||||
|
||||
T_R ((ret == -1), IPC_ERROR_USOCK_SEND);
|
||||
*sent = ret;
|
||||
IPC_RETURN_NO_ERROR;
|
||||
}
|
||||
|
@ -35,51 +100,58 @@ struct ipc_error usock_recv (const int32_t fd, char **buf, size_t * len)
|
|||
T_R ((buf == NULL), IPC_ERROR_USOCK_RECV__NO_BUFFER);
|
||||
T_R ((len == NULL), IPC_ERROR_USOCK_RECV__NO_LENGTH);
|
||||
|
||||
// printf("USOCKET: listen to %d (up to %lu bytes)\n", fd, *len);
|
||||
int32_t ret_recv = 0;
|
||||
|
||||
if (*len == 0)
|
||||
*len = IPC_MAX_MESSAGE_SIZE;
|
||||
|
||||
if (*buf == NULL) {
|
||||
// do not allocate too much memory
|
||||
if (*len > IPC_MAX_MESSAGE_SIZE) {
|
||||
*len = IPC_MAX_MESSAGE_SIZE;
|
||||
}
|
||||
SECURE_BUFFER_HEAP_ALLOCATION (*buf, *len + IPC_HEADER_SIZE,,
|
||||
IPC_RETURN_ERROR (IPC_ERROR_USOCK_RECV__HEAP_ALLOCATION));
|
||||
}
|
||||
|
||||
// msize_read: size of the message (without the header).
|
||||
uint32_t msize = 0;
|
||||
|
||||
// msize_read: size sum of the packets received.
|
||||
uint32_t msize_read = 0;
|
||||
|
||||
do {
|
||||
/**
|
||||
* recv:
|
||||
* ret > 0: message receveid
|
||||
* ret == 0: fd is closing
|
||||
* ret < 0: error
|
||||
*/
|
||||
ret_recv = recv (fd, *buf, *len, 0);
|
||||
#ifdef IPC_DEBUG
|
||||
|
||||
if (ret_recv > 0) {
|
||||
#ifdef IPC_DEBUG
|
||||
print_hexa ("msg recv", (uint8_t *) * buf, ret_recv);
|
||||
fflush (stdout);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (ret_recv > 0) {
|
||||
if (msize == 0) {
|
||||
memcpy (&msize, *buf + 1, sizeof msize);
|
||||
}
|
||||
msize = ntohl (msize);
|
||||
msize = ntohl (msize);
|
||||
|
||||
if (msize >= IPC_MAX_MESSAGE_SIZE) {
|
||||
#ifdef __PRINT_MSG_SIZES
|
||||
fprintf (stderr, "a %10u-byte message should be received on %d\n"
|
||||
, msize + IPC_HEADER_SIZE
|
||||
, fd);
|
||||
#endif
|
||||
}
|
||||
// else {
|
||||
// printf ("USOCKET: We received a message in (at least) two packets (receveid %u bytes).\n", msize_read);
|
||||
// }
|
||||
|
||||
if (msize > IPC_MAX_MESSAGE_SIZE) {
|
||||
#ifdef IPC_DEBUG
|
||||
print_hexa ("msg recv", (uint8_t *) * buf, ret_recv);
|
||||
fflush (stdout);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Do not allow messages with a longer size than expected.
|
||||
T_R ((msize > IPC_MAX_MESSAGE_SIZE), IPC_ERROR_USOCK_RECV__MESSAGE_SIZE);
|
||||
msize_read += ret_recv - IPC_HEADER_SIZE;
|
||||
|
||||
msize_read += ret_recv;
|
||||
} else if (ret_recv < 0) {
|
||||
if (*buf != NULL) {
|
||||
free (*buf);
|
||||
*buf = NULL;
|
||||
}
|
||||
*len = 0;
|
||||
|
||||
switch (errno) {
|
||||
|
@ -124,16 +196,18 @@ struct ipc_error usock_recv (const int32_t fd, char **buf, size_t * len)
|
|||
, "usock_recv: recv < 0, is the message size malformed?");
|
||||
}
|
||||
|
||||
} while (msize > msize_read);
|
||||
// if (msize > msize_read) {
|
||||
// printf ("USOCKET: loop again for %d (read %u/%u)\n", fd, msize_read, msize);
|
||||
// }
|
||||
|
||||
*len = msize + IPC_HEADER_SIZE;
|
||||
// In case msize still is 0, recv didn't worked as expected.
|
||||
} while (msize > 0 && msize > msize_read - IPC_HEADER_SIZE);
|
||||
|
||||
// 1 on none byte received, indicates a closed recipient
|
||||
// printf("USOCKET: end of the loop for client %d -- %u bytes read\n", fd, msize_read);
|
||||
*len = msize_read;
|
||||
|
||||
// none bytes received, indicates a closed recipient
|
||||
if (ret_recv == 0) {
|
||||
if (*buf != NULL) {
|
||||
free (*buf);
|
||||
*buf = NULL;
|
||||
}
|
||||
*len = 0;
|
||||
IPC_RETURN_ERROR (IPC_ERROR_CLOSED_RECIPIENT);
|
||||
}
|
||||
|
@ -152,7 +226,7 @@ struct ipc_error usock_connect (int32_t * fd, const char *path)
|
|||
int32_t sfd;
|
||||
socklen_t peer_addr_size = sizeof (struct sockaddr_un);
|
||||
|
||||
T_PERROR_RIPC (((sfd = socket (AF_UNIX, SOCK_SEQPACKET, 0)) == -1), "socket", IPC_ERROR_USOCK_CONNECT__SOCKET);
|
||||
T_PERROR_RIPC (((sfd = socket (AF_UNIX, SOCK_STREAM, 0)) == -1), "socket", IPC_ERROR_USOCK_CONNECT__SOCKET);
|
||||
strncpy (my_addr.sun_path, path, (strlen (path) < PATH_MAX) ? strlen (path) : PATH_MAX);
|
||||
|
||||
TEST_IPC_RETURN_ON_ERROR(directory_setup_ (path));
|
||||
|
@ -186,7 +260,7 @@ struct ipc_error usock_init (int32_t * fd, const char *path)
|
|||
|
||||
TEST_IPC_RETURN_ON_ERROR(directory_setup_ (path));
|
||||
|
||||
T_PERROR_RIPC (((sfd = socket (AF_UNIX, SOCK_SEQPACKET, 0)) == -1)
|
||||
T_PERROR_RIPC (((sfd = socket (AF_UNIX, SOCK_STREAM, 0)) == -1)
|
||||
, "socket", IPC_ERROR_USOCK_INIT__WRONG_FILE_DESCRIPTOR);
|
||||
|
||||
// delete the unix socket if already created
|
||||
|
|
|
@ -6,30 +6,33 @@
|
|||
|
||||
#define SERVICE_NAME "pong"
|
||||
|
||||
int main(int argc, char * argv[], char **env)
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
argc = (int) argc;
|
||||
argv = (char **) argv;
|
||||
|
||||
SECURE_DECLARATION(struct ipc_error, ret);
|
||||
SECURE_DECLARATION(struct ipc_connection_info,service);
|
||||
SECURE_DECLARATION(struct ipc_ctx, ctx);
|
||||
SECURE_DECLARATION(struct ipc_event, event);
|
||||
|
||||
ret = ipc_connection (env, &service, SERVICE_NAME);
|
||||
|
||||
ret = ipc_connection (&ctx, SERVICE_NAME, NULL);
|
||||
if (ret.error_code != IPC_ERROR_NONE) {
|
||||
printf ("error: %s\n", ipc_errors_get(ret.error_code));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// long timer = 10;
|
||||
// int timer = 10000; // 10 seconds
|
||||
// ret = ipc_wait_event (services, struct ipc_event *event, &timer);
|
||||
// if (ret.error_code != IPC_ERROR_NONE) {
|
||||
// return EXIT_FAILURE;
|
||||
// }
|
||||
|
||||
ret = ipc_close (&service);
|
||||
ret = ipc_close_all (&ctx);
|
||||
if (ret.error_code != IPC_ERROR_NONE) {
|
||||
printf ("error: %s\n", ipc_errors_get(ret.error_code));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
ipc_ctx_free (&ctx);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -6,31 +6,31 @@
|
|||
|
||||
#define SERVICE_NAME "pong"
|
||||
|
||||
int main(int argc, char * argv[], char **env)
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
argc = (int) argc;
|
||||
argv = (char **) argv;
|
||||
|
||||
SECURE_DECLARATION(struct ipc_connection_info,srv);
|
||||
long timer = 10;
|
||||
|
||||
SECURE_DECLARATION(struct ipc_ctx, ctx);
|
||||
int timer = 10000; // 10 seconds timer
|
||||
|
||||
printf ("func 01 - server init...\n");
|
||||
TEST_IPC_Q(ipc_server_init (env, &srv, SERVICE_NAME), EXIT_FAILURE);
|
||||
|
||||
TEST_IPC_Q(ipc_server_init (&ctx, SERVICE_NAME), EXIT_FAILURE);
|
||||
|
||||
printf ("func 01 - server init ok\n");
|
||||
SECURE_DECLARATION(struct ipc_connection_infos, clients);
|
||||
SECURE_DECLARATION(struct ipc_event,event);
|
||||
SECURE_DECLARATION(struct ipc_event, event);
|
||||
|
||||
printf ("func 01 - service polling...\n");
|
||||
|
||||
// listen only for a single client
|
||||
TEST_IPC_Q(ipc_wait_event (&clients, &srv, &event, &timer), EXIT_FAILURE);
|
||||
TEST_IPC_Q(ipc_wait_event (&ctx, &event, &timer), EXIT_FAILURE);
|
||||
|
||||
switch (event.type) {
|
||||
case IPC_EVENT_TYPE_TIMER : {
|
||||
fprintf(stderr, "time up!\n");
|
||||
|
||||
timer = 10;
|
||||
timer = 10000;
|
||||
};
|
||||
break;
|
||||
case IPC_EVENT_TYPE_CONNECTION :
|
||||
|
@ -49,11 +49,11 @@ int main(int argc, char * argv[], char **env)
|
|||
break;
|
||||
}
|
||||
|
||||
printf ("func 01 - closing clients...\n");
|
||||
ipc_connections_free (&clients);
|
||||
|
||||
printf ("func 01 - closing server...\n");
|
||||
TEST_IPC_Q(ipc_server_close(&srv), EXIT_FAILURE);
|
||||
TEST_IPC_Q(ipc_close_all(&ctx), EXIT_FAILURE);
|
||||
|
||||
printf ("func 01 - closing ctx...\n");
|
||||
ipc_ctx_free (&ctx);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -13,48 +13,50 @@
|
|||
fprintf(stderr, "error while %s: %s\n", msg, err);\
|
||||
}
|
||||
|
||||
void non_interactive (char *env[])
|
||||
void non_interactive ()
|
||||
{
|
||||
struct ipc_message m;
|
||||
memset (&m, 0, sizeof (struct ipc_message));
|
||||
SECURE_DECLARATION(struct ipc_connection_info, srv);
|
||||
SECURE_DECLARATION(struct ipc_message, m);
|
||||
SECURE_DECLARATION(struct ipc_ctx, ctx);
|
||||
|
||||
// init service
|
||||
TEST_IPC_Q(ipc_connection (env, &srv, SERVICE_NAME), EXIT_FAILURE);
|
||||
TEST_IPC_Q(ipc_connection (&ctx, SERVICE_NAME, NULL), EXIT_FAILURE);
|
||||
|
||||
printf ("msg to send (%ld): %.*s\n", (ssize_t) strlen(MSG) +1, (int) strlen(MSG), MSG);
|
||||
int server_fd = ctx.pollfd[0].fd;
|
||||
|
||||
printf ("msg for fd %d to send (%ld): %.*s\n"
|
||||
, server_fd
|
||||
, (ssize_t) strlen(MSG) +1, (int) strlen(MSG), MSG);
|
||||
TEST_IPC_Q(ipc_message_format_data (&m, /* type */ 'a', MSG, (ssize_t) strlen(MSG) +1), EXIT_FAILURE);
|
||||
TEST_IPC_Q(ipc_write (&srv, &m), EXIT_FAILURE);
|
||||
TEST_IPC_Q(ipc_read (&srv, &m), EXIT_FAILURE);
|
||||
|
||||
m.fd = server_fd;
|
||||
TEST_IPC_Q(ipc_write_fd (server_fd, &m), EXIT_FAILURE);
|
||||
TEST_IPC_Q(ipc_read (&ctx, 0 /* only one option */, &m), EXIT_FAILURE);
|
||||
|
||||
printf ("msg recv: %s\n", m.payload);
|
||||
ipc_message_empty (&m);
|
||||
|
||||
TEST_IPC_Q(ipc_close (&srv), EXIT_FAILURE);
|
||||
TEST_IPC_Q(ipc_close_all (&ctx), EXIT_FAILURE);
|
||||
ipc_ctx_free (&ctx);
|
||||
}
|
||||
|
||||
void interactive (char *env[])
|
||||
#if 0
|
||||
void interactive ()
|
||||
{
|
||||
SECURE_DECLARATION(struct ipc_connection_info, srv);
|
||||
|
||||
// index and version should be filled
|
||||
srv.index = 0;
|
||||
srv.version = 0;
|
||||
SECURE_DECLARATION(struct ipc_ctx, ctx);
|
||||
|
||||
// init service
|
||||
TEST_IPC_Q(ipc_connection (env, &srv, SERVICE_NAME), EXIT_FAILURE);
|
||||
TEST_IPC_Q(ipc_connection (&ctx, SERVICE_NAME), EXIT_FAILURE);
|
||||
|
||||
int server_fd = ctx.pollfd[0].fd;
|
||||
|
||||
SECURE_DECLARATION(struct ipc_event, event);
|
||||
SECURE_DECLARATION(struct ipc_connection_infos, services);
|
||||
|
||||
TEST_IPC_Q(ipc_add (&services, &srv), EXIT_FAILURE);
|
||||
|
||||
long timer = 10;
|
||||
|
||||
while (1) {
|
||||
printf ("msg to send: ");
|
||||
fflush (stdout);
|
||||
TEST_IPC_Q(ipc_wait_event (&services, NULL, &event, &timer), EXIT_FAILURE);
|
||||
TEST_IPC_Q(ipc_wait_event (&ctx, &event, &timer), EXIT_FAILURE);
|
||||
|
||||
switch (event.type) {
|
||||
case IPC_EVENT_TYPE_TIMER: {
|
||||
|
@ -71,14 +73,16 @@ void interactive (char *env[])
|
|||
ipc_message_empty (m);
|
||||
free (m);
|
||||
|
||||
ipc_connections_free (&services);
|
||||
TEST_IPC_Q(ipc_close_all (&ctx), EXIT_FAILURE);
|
||||
|
||||
TEST_IPC_Q(ipc_close (&srv), EXIT_FAILURE);
|
||||
ipc_ctx_free (&ctx);
|
||||
|
||||
exit (EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
TEST_IPC_Q(ipc_write (&srv, m), EXIT_FAILURE);
|
||||
m->fd = server_fd;
|
||||
|
||||
TEST_IPC_Q(ipc_write (&ctx, m), EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
case IPC_EVENT_TYPE_MESSAGE:
|
||||
|
@ -96,16 +100,19 @@ void interactive (char *env[])
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int main (int argc, char *argv[], char *env[])
|
||||
//int main (int argc, char *argv[])
|
||||
int main (void)
|
||||
{
|
||||
argc = argc; // warnings
|
||||
argv = argv; // warnings
|
||||
non_interactive ();
|
||||
|
||||
#if 0
|
||||
if (argc == 1)
|
||||
non_interactive (env);
|
||||
non_interactive ();
|
||||
else
|
||||
interactive (env);
|
||||
interactive ();
|
||||
#endif
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -14,71 +14,67 @@
|
|||
|
||||
int cpt = 0;
|
||||
|
||||
struct ipc_connection_info *srv = 0;
|
||||
struct ipc_connection_infos *clients;
|
||||
struct ipc_ctx *ctx;
|
||||
|
||||
|
||||
void main_loop ()
|
||||
{
|
||||
SECURE_DECLARATION(struct ipc_error, ret);
|
||||
SECURE_DECLARATION(struct ipc_event, event);
|
||||
|
||||
clients = malloc (sizeof (struct ipc_connection_infos));
|
||||
memset(clients, 0, sizeof(struct ipc_connection_infos));
|
||||
|
||||
SECURE_DECLARATION(struct ipc_event,event);
|
||||
|
||||
long timer = 10;
|
||||
int timer = 10000;
|
||||
|
||||
while(1) {
|
||||
// ipc_wait_event provides one event at a time
|
||||
// warning: event->m is free'ed if not NULL
|
||||
ret = ipc_wait_event (clients, srv, &event, &timer);
|
||||
ret = ipc_wait_event (ctx, &event, &timer);
|
||||
if (ret.error_code != IPC_ERROR_NONE && ret.error_code != IPC_ERROR_CLOSED_RECIPIENT) {
|
||||
PRINTERR(ret,"service poll event");
|
||||
|
||||
// the application will shut down, and close the service
|
||||
TEST_IPC_Q(ipc_server_close (srv), EXIT_FAILURE);
|
||||
TEST_IPC_Q(ipc_close_all (ctx), EXIT_FAILURE);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
switch (event.type) {
|
||||
case IPC_EVENT_TYPE_TIMER: {
|
||||
fprintf(stderr, "time up!\n");
|
||||
timer = 10;
|
||||
timer = 10000;
|
||||
};
|
||||
break;
|
||||
case IPC_EVENT_TYPE_CONNECTION: {
|
||||
cpt++;
|
||||
printf ("connection: %d clients connected\n", cpt);
|
||||
printf ("new client has the fd %d\n", ((struct ipc_connection_info*) event.origin)->fd);
|
||||
// printf ("new connection (fd %d): %d ctx connected\n", event.origin, cpt);
|
||||
};
|
||||
break;
|
||||
case IPC_EVENT_TYPE_DISCONNECTION:
|
||||
{
|
||||
cpt--;
|
||||
printf ("disconnection: %d clients remaining\n", cpt);
|
||||
|
||||
// free the ipc_connection_info structure
|
||||
free (event.origin);
|
||||
// printf ("disconnection (fd %d): %d clients remaining\n", event.origin, cpt);
|
||||
};
|
||||
break;
|
||||
case IPC_EVENT_TYPE_MESSAGE:
|
||||
{
|
||||
struct ipc_message *m = event.m;
|
||||
if (m->length > 0) {
|
||||
printf ("message received (type %d): %.*s\n", m->type, m->length, m->payload);
|
||||
// printf ("message received (type %d): %.*s\n", m->type, m->length, m->payload);
|
||||
}
|
||||
|
||||
ret = ipc_write (event.origin, m);
|
||||
m->fd = event.origin;
|
||||
ret = ipc_write (ctx, m);
|
||||
if (ret.error_code != IPC_ERROR_NONE) {
|
||||
PRINTERR(ret,"server write");
|
||||
}
|
||||
};
|
||||
break;
|
||||
case IPC_EVENT_TYPE_TX:
|
||||
{
|
||||
// printf ("a message was sent\n");
|
||||
}
|
||||
break;
|
||||
case IPC_EVENT_TYPE_ERROR:
|
||||
{
|
||||
fprintf (stderr, "a problem happened with client %d\n"
|
||||
, ((struct ipc_connection_info*) event.origin)->fd);
|
||||
fprintf (stderr, "a problem happened with client %d\n", event.origin);
|
||||
};
|
||||
break;
|
||||
default :
|
||||
|
@ -97,48 +93,34 @@ void exit_program(int signal)
|
|||
{
|
||||
printf("Quitting, signal: %d\n", signal);
|
||||
|
||||
// free remaining clients
|
||||
for (size_t i = 0; i < clients->size ; i++) {
|
||||
struct ipc_connection_info *cli = clients->cinfos[i];
|
||||
if (cli != NULL) {
|
||||
free (cli);
|
||||
}
|
||||
clients->cinfos[i] = NULL;
|
||||
}
|
||||
|
||||
ipc_connections_free (clients);
|
||||
free (clients);
|
||||
|
||||
|
||||
// the application will shut down, and close the service
|
||||
TEST_IPC_Q(ipc_server_close (srv), EXIT_FAILURE);
|
||||
free (srv);
|
||||
TEST_IPC_Q(ipc_close_all (ctx), EXIT_FAILURE);
|
||||
|
||||
// free remaining ctx
|
||||
ipc_ctx_free (ctx);
|
||||
free (ctx);
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
/*
|
||||
* service ping-pong: send back everything sent by the clients
|
||||
* service ping-pong: send back everything sent by the ctx
|
||||
* stop the program on SIG{TERM,INT,ALRM,USR{1,2},HUP} signals
|
||||
*/
|
||||
|
||||
int main(int argc, char * argv[], char **env)
|
||||
int main(void)
|
||||
{
|
||||
argc = argc; // warnings
|
||||
argv = argv; // warnings
|
||||
|
||||
printf ("pid = %d\n", getpid ());
|
||||
|
||||
srv = malloc (sizeof (struct ipc_connection_info));
|
||||
if (srv == NULL) {
|
||||
ctx = malloc (sizeof (struct ipc_ctx));
|
||||
if (ctx == NULL) {
|
||||
exit (1);
|
||||
}
|
||||
memset (srv, 0, sizeof (struct ipc_connection_info));
|
||||
srv->index = 0;
|
||||
srv->version = 0;
|
||||
memset (ctx, 0, sizeof (struct ipc_ctx));
|
||||
|
||||
TEST_IPC_Q(ipc_server_init (env, srv, PONGD_SERVICE_NAME), EXIT_FAILURE);
|
||||
TEST_IPC_Q(ipc_server_init (ctx, PONGD_SERVICE_NAME), EXIT_FAILURE);
|
||||
|
||||
struct ipc_connection_info * srv = &ctx->cinfos[0];
|
||||
printf ("Listening on %s.\n", srv->spath);
|
||||
|
||||
printf("MAIN: server created\n" );
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
// test the behavior of the server when the client never read its messages
|
||||
|
||||
void send_message (struct ipc_connection_info *ci)
|
||||
void send_message (struct ipc_ctx *ctx)
|
||||
{
|
||||
SECURE_DECLARATION (struct ipc_message, m);
|
||||
SECURE_MALLOC (m.payload, 5, exit(EXIT_FAILURE));
|
||||
|
@ -18,23 +18,24 @@ void send_message (struct ipc_connection_info *ci)
|
|||
m.user_type = 42;
|
||||
m.length = 5;
|
||||
|
||||
ipc_write (ci, &m);
|
||||
ipc_write_fd (ctx->pollfd[0].fd /* only one connection */, &m);
|
||||
|
||||
ipc_message_empty (&m);
|
||||
}
|
||||
|
||||
|
||||
void read_message (struct ipc_connection_info *ci)
|
||||
void read_message (struct ipc_ctx *ctx)
|
||||
{
|
||||
#if 0
|
||||
SECURE_DECLARATION(struct ipc_event, event);
|
||||
SECURE_DECLARATION(struct ipc_connection_infos, clients);
|
||||
SECURE_DECLARATION (struct ipc_message, m);
|
||||
|
||||
long timer = 10;
|
||||
int timer = 10000;
|
||||
|
||||
TEST_IPC_Q(ipc_read (ci, &m), EXIT_FAILURE);
|
||||
// ctx, index, message
|
||||
TEST_IPC_Q(ipc_read (ctx, 0 /* only one server here */, &m), EXIT_FAILURE);
|
||||
|
||||
ipc_wait_event (&clients, NULL, &event, &timer);
|
||||
ipc_wait_event (ctx, &event, &timer);
|
||||
|
||||
switch (event.type) {
|
||||
case IPC_EVENT_TYPE_MESSAGE : {
|
||||
|
@ -52,37 +53,36 @@ void read_message (struct ipc_connection_info *ci)
|
|||
break;
|
||||
}
|
||||
|
||||
ipc_connections_free (&clients);
|
||||
ipc_ctx_free (ctx);
|
||||
#else
|
||||
SECURE_DECLARATION (struct ipc_message, m);
|
||||
|
||||
TEST_IPC_Q(ipc_read (ci, &m), EXIT_FAILURE);
|
||||
printf ("received message: %*.s\n", m.length, m.payload);
|
||||
TEST_IPC_Q(ipc_read (ctx, 0 /* only one server here */, &m), EXIT_FAILURE);
|
||||
printf ("received message: %.*s\n", m.length, m.payload);
|
||||
free (m.payload);
|
||||
#endif
|
||||
}
|
||||
|
||||
int main(int argc, char * argv[], char **env)
|
||||
int main(void)
|
||||
{
|
||||
argc = argc;
|
||||
argv = argv;
|
||||
|
||||
SECURE_DECLARATION(struct ipc_connection_info,srv1);
|
||||
SECURE_DECLARATION(struct ipc_connection_info,srv2);
|
||||
SECURE_DECLARATION(struct ipc_ctx, ctx1);
|
||||
SECURE_DECLARATION(struct ipc_ctx, ctx2);
|
||||
SECURE_DECLARATION(struct ipc_event, event);
|
||||
|
||||
TEST_IPC_Q (ipc_connection (env, &srv1, SERVICE_NAME), EXIT_FAILURE);
|
||||
TEST_IPC_Q (ipc_connection (env, &srv2, SERVICE_NAME), EXIT_FAILURE);
|
||||
TEST_IPC_Q (ipc_connection (&ctx1, SERVICE_NAME, NULL), EXIT_FAILURE);
|
||||
TEST_IPC_Q (ipc_connection (&ctx2, SERVICE_NAME, NULL), EXIT_FAILURE);
|
||||
|
||||
send_message (&srv1);
|
||||
read_message (&srv1);
|
||||
send_message (&ctx1);
|
||||
read_message (&ctx1);
|
||||
|
||||
TEST_IPC_Q (ipc_close (&srv1), EXIT_FAILURE);
|
||||
TEST_IPC_Q (ipc_close_all (&ctx1), EXIT_FAILURE);
|
||||
ipc_ctx_free (&ctx1);
|
||||
|
||||
send_message (&srv2);
|
||||
read_message (&srv2);
|
||||
send_message (&ctx2);
|
||||
read_message (&ctx2);
|
||||
|
||||
TEST_IPC_Q (ipc_close (&srv2), EXIT_FAILURE);
|
||||
TEST_IPC_Q (ipc_close_all (&ctx2), EXIT_FAILURE);
|
||||
ipc_ctx_free (&ctx2);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -6,44 +6,72 @@
|
|||
#include <string.h>
|
||||
|
||||
#define SERVICE_NAME "pong"
|
||||
struct ipc_ctx *pctx = NULL;
|
||||
|
||||
int main_loop(int argc, char * argv[], char **env)
|
||||
void exit_program(int signal)
|
||||
{
|
||||
printf("Quitting, signal: %d\n", signal);
|
||||
|
||||
// the application will shut down, and close the service
|
||||
TEST_IPC_Q(ipc_close_all (pctx), EXIT_FAILURE);
|
||||
|
||||
// free remaining ctx
|
||||
ipc_ctx_free (pctx);
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
int main_loop(int argc, char * argv[])
|
||||
{
|
||||
argc = argc;
|
||||
argv = argv;
|
||||
|
||||
SECURE_DECLARATION (struct ipc_connection_info, srv);
|
||||
long timer = 10;
|
||||
SECURE_DECLARATION (struct ipc_ctx, ctx);
|
||||
pctx = &ctx;
|
||||
int timer = 10000; // in ms
|
||||
|
||||
printf ("func 03 - server init...\n");
|
||||
TEST_IPC_Q (ipc_server_init (env, &srv, SERVICE_NAME), EXIT_FAILURE);
|
||||
TEST_IPC_Q (ipc_server_init (&ctx, SERVICE_NAME), EXIT_FAILURE);
|
||||
printf ("func 03 - server init ok\n");
|
||||
|
||||
SECURE_DECLARATION (struct ipc_connection_infos, clients);
|
||||
SECURE_DECLARATION (struct ipc_event, event);
|
||||
|
||||
printf ("func 01 - service polling...\n");
|
||||
// listen only for a single client
|
||||
while (1) {
|
||||
TEST_IPC_WAIT_EVENT_Q (ipc_wait_event (&clients, &srv, &event, &timer), EXIT_FAILURE);
|
||||
// TEST_IPC_WAIT_EVENT_Q (ipc_wait_event (&ctx, &event, &timer), EXIT_FAILURE);
|
||||
struct ipc_error ret = ipc_wait_event (&ctx, &event, &timer);
|
||||
|
||||
if (ret.error_code != IPC_ERROR_NONE &&
|
||||
ret.error_code != IPC_ERROR_CLOSED_RECIPIENT) {
|
||||
printf ("An error happened :(\n");
|
||||
}
|
||||
|
||||
switch (event.type) {
|
||||
case IPC_EVENT_TYPE_TIMER : {
|
||||
fprintf (stderr, "time up!");
|
||||
timer = 10;
|
||||
fprintf (stderr, "time up!\n");
|
||||
timer = 10000;
|
||||
}
|
||||
break;
|
||||
case IPC_EVENT_TYPE_CONNECTION : {
|
||||
printf ("connection establishment: %d \n", event.origin->fd);
|
||||
printf ("connection establishment: %d \n", event.origin);
|
||||
}
|
||||
break;
|
||||
case IPC_EVENT_TYPE_DISCONNECTION : {
|
||||
printf ("client %d disconnecting\n", event.origin->fd);
|
||||
printf ("client %d disconnecting\n", event.origin);
|
||||
};
|
||||
break;
|
||||
case IPC_EVENT_TYPE_MESSAGE : {
|
||||
printf ("received message: %s\n", ((struct ipc_message*) event.m)->payload);
|
||||
ipc_write (event.origin, (struct ipc_message*) event.m);
|
||||
struct ipc_message *m = (struct ipc_message*) event.m;
|
||||
printf ("received message (%d bytes): %.*s\n"
|
||||
, m->length
|
||||
, m->length
|
||||
, m->payload);
|
||||
ipc_write (&ctx, m);
|
||||
}
|
||||
break;
|
||||
case IPC_EVENT_TYPE_TX : {
|
||||
printf ("message sent to fd %d\n", event.origin);
|
||||
}
|
||||
break;
|
||||
case IPC_EVENT_TYPE_NOT_SET :
|
||||
|
@ -51,34 +79,30 @@ int main_loop(int argc, char * argv[], char **env)
|
|||
case IPC_EVENT_TYPE_EXTRA_SOCKET :
|
||||
default :
|
||||
printf ("not ok - should not happen\n");
|
||||
exit (EXIT_FAILURE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
printf ("func 03 - closing clients...\n");
|
||||
ipc_connections_free (&clients);
|
||||
printf ("func 03 - closing server...\n");
|
||||
TEST_IPC_Q (ipc_server_close(&srv), EXIT_FAILURE);
|
||||
TEST_IPC_Q (ipc_close_all(&ctx), EXIT_FAILURE);
|
||||
|
||||
printf ("func 03 - freeing the context\n");
|
||||
ipc_ctx_free (&ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void exit_program(int signal)
|
||||
{
|
||||
printf("Quitting, signal: %d\n", signal);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char * argv[], char **env)
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
// In case we want to quit the program, do it cleanly.
|
||||
signal (SIGHUP, exit_program);
|
||||
signal (SIGALRM, exit_program);
|
||||
signal (SIGUSR1, exit_program);
|
||||
signal (SIGUSR2, exit_program);
|
||||
signal (SIGTERM, exit_program);
|
||||
signal (SIGINT, exit_program);
|
||||
main_loop (argc, argv, env);
|
||||
|
||||
main_loop (argc, argv);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
// test the behavior of the server when the client never read its messages
|
||||
|
||||
void send_message (struct ipc_connection_info *ci)
|
||||
void send_message (struct ipc_ctx *ctx)
|
||||
{
|
||||
SECURE_DECLARATION (struct ipc_message, m);
|
||||
SECURE_MALLOC (m.payload, 1, exit(EXIT_FAILURE));
|
||||
|
@ -17,18 +17,20 @@ void send_message (struct ipc_connection_info *ci)
|
|||
m.type = MSG_TYPE_DATA;
|
||||
m.user_type = 42;
|
||||
m.length = 0;
|
||||
m.fd = ctx->pollfd[0].fd;
|
||||
|
||||
ipc_write (ci, &m);
|
||||
// ipc_write_fd = write now, without waiting the fd to become available
|
||||
ipc_write_fd (ctx->pollfd[0].fd, &m);
|
||||
|
||||
ipc_message_empty (&m);
|
||||
}
|
||||
|
||||
|
||||
void read_message (struct ipc_connection_info *ci)
|
||||
void read_message (struct ipc_ctx *ctx)
|
||||
{
|
||||
SECURE_DECLARATION (struct ipc_message, m);
|
||||
|
||||
ipc_read (ci, &m);
|
||||
ipc_read (ctx, 0 /* there is only one valid index */, &m);
|
||||
if (m.length > 0) {
|
||||
printf ("received message: %*.s\n", m.length, m.payload);
|
||||
}
|
||||
|
@ -41,19 +43,17 @@ void read_message (struct ipc_connection_info *ci)
|
|||
free (m.payload);
|
||||
}
|
||||
|
||||
int main(int argc, char * argv[], char **env)
|
||||
int main(void)
|
||||
{
|
||||
argc = argc;
|
||||
argv = argv;
|
||||
SECURE_DECLARATION(struct ipc_ctx, ctx);
|
||||
|
||||
SECURE_DECLARATION(struct ipc_connection_info,srv1);
|
||||
TEST_IPC_Q(ipc_connection (&ctx, SERVICE_NAME, NULL), EXIT_FAILURE);
|
||||
|
||||
TEST_IPC_Q(ipc_connection (env, &srv1, SERVICE_NAME), EXIT_FAILURE);
|
||||
send_message (&ctx);
|
||||
read_message (&ctx);
|
||||
|
||||
send_message (&srv1);
|
||||
read_message (&srv1);
|
||||
|
||||
TEST_IPC_Q(ipc_close (&srv1), EXIT_FAILURE);
|
||||
TEST_IPC_Q(ipc_close_all (&ctx), EXIT_FAILURE);
|
||||
ipc_ctx_free(&ctx);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#define _BSD_SOURCE
|
||||
// #define _BSD_SOURCE
|
||||
#define _DEFAULT_SOURCE
|
||||
|
||||
#include "../src/ipc.h"
|
||||
|
||||
|
@ -12,9 +13,9 @@
|
|||
#define DEFAULT_MSG "coucou"
|
||||
|
||||
|
||||
int main(int argc, char * argv[], char **env)
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
SECURE_DECLARATION(struct ipc_connection_info,srv);
|
||||
SECURE_DECLARATION(struct ipc_ctx, ctx);
|
||||
SECURE_DECLARATION(struct ipc_event, event);
|
||||
|
||||
SECURE_DECLARATION (struct ipc_message, write_m);
|
||||
|
@ -46,20 +47,21 @@ int main(int argc, char * argv[], char **env)
|
|||
memcpy(message_str, DEFAULT_MSG, strlen(DEFAULT_MSG));
|
||||
}
|
||||
|
||||
TEST_IPC_Q (ipc_connection (env, &srv, SERVICE_NAME), EXIT_FAILURE);
|
||||
TEST_IPC_Q (ipc_connection (&ctx, SERVICE_NAME, NULL), EXIT_FAILURE);
|
||||
|
||||
SECURE_MALLOC (write_m.payload, strlen(message_str), exit(EXIT_FAILURE));
|
||||
memcpy (write_m.payload, message_str, strlen(message_str));
|
||||
write_m.type = MSG_TYPE_DATA;
|
||||
write_m.user_type = 42;
|
||||
write_m.fd = ctx.pollfd[0].fd;
|
||||
write_m.length = strlen(message_str);
|
||||
|
||||
gettimeofday(&tval_before, NULL);
|
||||
for (size_t i = 0 ; i < nb_rounds ; i++) {
|
||||
ipc_write (&srv, &write_m);
|
||||
ipc_write_fd (ctx.pollfd[0].fd, &write_m);
|
||||
|
||||
// reading
|
||||
TEST_IPC_Q(ipc_read (&srv, &read_m), EXIT_FAILURE);
|
||||
TEST_IPC_Q(ipc_read (&ctx, 0 /* only valid index */, &read_m), EXIT_FAILURE);
|
||||
// printf ("received message (%d bytes): %*s\n", read_m.length, read_m.length, read_m.payload);
|
||||
// ipc_message_empty (&read_m);
|
||||
|
||||
|
@ -71,7 +73,7 @@ int main(int argc, char * argv[], char **env)
|
|||
printf("Time elapsed: %ld.%06ld\n", (long int)tval_result.tv_sec, (long int)tval_result.tv_usec);
|
||||
|
||||
// disconnection
|
||||
TEST_IPC_Q (ipc_close (&srv), EXIT_FAILURE);
|
||||
TEST_IPC_Q (ipc_close_all (&ctx), EXIT_FAILURE);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -5,27 +5,21 @@
|
|||
#include <string.h>
|
||||
|
||||
#define SERVICE_NAME "example"
|
||||
#define VERSION 0
|
||||
#define INDEX 0
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
char * sname = SERVICE_NAME;
|
||||
int32_t index = INDEX;
|
||||
int32_t version = VERSION;
|
||||
|
||||
if (argc == 4) {
|
||||
if (argc == 2) {
|
||||
sname = argv[1];
|
||||
index = atoi(argv[2]);
|
||||
version = atoi(argv[3]);
|
||||
}
|
||||
else if (argc != 1) {
|
||||
fprintf (stderr, "usage: %s [service-name index version]\n", argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
service_path (path, sname, index, version);
|
||||
service_path (path, sname);
|
||||
|
||||
// printf ("servicename: %s, index: %d, version: %d\n", sname, index, version);
|
||||
printf ("%s\n", path);
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
zig-cache
|
||||
zig-out
|
||||
docs
|
||||
build
|
||||
ipcd
|
||||
tcpd
|
||||
pongd
|
||||
pong
|
|
@ -0,0 +1,44 @@
|
|||
# Why rewrite in Zig
|
||||
|
||||
I did a library called `libipc` then I wanted to rewrite it in Zig.
|
||||
|
||||
The problems with the C-based version:
|
||||
|
||||
- libipc cannot easily be ported on other plateforms
|
||||
* cannot compile under musl
|
||||
* cannot compile for any OS
|
||||
* different libc = different error codes for functions
|
||||
- cannot change the code easily without risking to break stuff
|
||||
- behavior isn't the same accross OSs
|
||||
* glibc-based and musl-based Linux have different behavior
|
||||
* Linux and OpenBSD have different behavior
|
||||
- hard to debug
|
||||
* OpenBSD has a buggy valgrind
|
||||
|
||||
For all these reasons, this library took a lot of time to dev (~ 2 months), and I'm still not entirely confident in its reliability.
|
||||
Tests are required to gain confidence.
|
||||
The mere compilation isn't a proof of anything.
|
||||
And the code size (2kloc) for something that simple can be seen as a mess, despite being rather fair.
|
||||
|
||||
What Zig brings to the table:
|
||||
|
||||
- OS and libc-agnostic, all quirks are handled in the std lib by the Zig dev team
|
||||
- same source code for all OSs
|
||||
- can compile **from and to** any OS and libc
|
||||
- errors already are verified in the std lib
|
||||
- compiling `foo() catch |e| switch(e) {}` shows all possible errors
|
||||
- std lib provides
|
||||
* basic structures and functions, no need to add deps or write functions to have simple lists and such
|
||||
* memory allocators & checks, no need for valgrind and other memory checking tools
|
||||
* basic network structures (address & stuff)
|
||||
- structures can be printed
|
||||
- code and tests in the same file
|
||||
- (in a near future) async functions, to handle networking operations in a very elegant manner
|
||||
|
||||
All these features provide a **massive gain of time**.
|
||||
|
||||
And in general, just having the same standard library for all OSs is great just not to have to rely on different implementations and even locations of code and header files.
|
||||
|
||||
What isn't *that* great with Zig:
|
||||
|
||||
- structures can be printed, but they also provide the format themselves, leading to an unreliable way of discovering structure's content.
|
|
@ -0,0 +1,38 @@
|
|||
### MISC
|
||||
|
||||
- create the unix socket directory
|
||||
- close the connection and log when we receive too big messages
|
||||
- decide then explicitely document what the max message size should be
|
||||
- rx message buffer should be small but should grow if required
|
||||
- some functions are not exposed in the bindings, mostly functions related to switching
|
||||
|
||||
### makefile
|
||||
|
||||
- release
|
||||
- distribution
|
||||
|
||||
### documentation
|
||||
|
||||
- document the two ways to use LibIPC: either within some Zig code or through the bindings
|
||||
- manpages for ipcd, tcpd, pong, pongd
|
||||
|
||||
### src/exchange-fd.zig
|
||||
|
||||
- still very WIP, even though it works as expected
|
||||
- recvmsg is a very *stupid* copy of the sendmsg fn, **EXPECT ERRORS** (if used outside libipc)
|
||||
- at least one memory error when using Cmsghdr (see below)
|
||||
|
||||
==32374== Syscall param sendmsg(msg.msg_control) points to uninitialised byte(s)
|
||||
==32374== at 0x40554A3: ??? (in /lib/ld-musl-x86_64.so.1)
|
||||
==32374== by 0x40526F9: ??? (in /lib/ld-musl-x86_64.so.1)
|
||||
==32374== by 0x4096B83: ???
|
||||
==32374== Address 0x1ffefff384 is on thread 1's stack
|
||||
==32374== Uninitialised value was created by a client request
|
||||
==32374== at 0x289769: exchange-fd.Cmsghdr(i32).init (exchange-fd.zig:39)
|
||||
==32374== by 0x2808F0: exchange-fd.send_fd (exchange-fd.zig:86)
|
||||
==32374== by 0x27EA97: ipcd.create_service (ipcd.zig:178)
|
||||
==32374== by 0x28117C: ipcd.main (ipcd.zig:224)
|
||||
==32374== by 0x28161E: callMain (start.zig:614)
|
||||
==32374== by 0x28161E: initEventLoopAndCallMain (start.zig:548)
|
||||
==32374== by 0x28161E: callMainWithArgs (start.zig:498)
|
||||
==32374== by 0x28161E: main (start.zig:513)
|
|
@ -0,0 +1,226 @@
|
|||
const std = @import("std");
|
||||
const net = std.net;
|
||||
const fmt = std.fmt;
|
||||
const os = std.os;
|
||||
|
||||
const ipc = @import("ipc");
|
||||
const hexdump = ipc.hexdump;
|
||||
const Message = ipc.Message;
|
||||
|
||||
// Import send_fd this way in order to produce docs for exchange-fd functions.
|
||||
const exchange_fd = ipc.exchangefd;
|
||||
const send_fd = exchange_fd.send_fd;
|
||||
|
||||
const builtin = @import("builtin");
|
||||
const native_os = builtin.target.os.tag;
|
||||
const print = std.debug.print;
|
||||
const testing = std.testing;
|
||||
const print_eq = ipc.util.print_eq;
|
||||
const URI = ipc.util.URI;
|
||||
|
||||
// Standard library is unecessary complex regarding networking.
|
||||
// libipc drops it and uses plain old file descriptors instead.
|
||||
|
||||
// API should completely obfuscate the inner structures.
|
||||
// Only libipc structures should be necessary to write any networking code,
|
||||
// users should only work with Context and Message, mostly.
|
||||
|
||||
// QUESTION: should libipc use std.fs.path and not simple [] const u8?
|
||||
|
||||
fn create_service() !void {
|
||||
const config = .{.safety = true};
|
||||
var gpa = std.heap.GeneralPurposeAllocator(config){};
|
||||
defer _ = gpa.deinit();
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
var ctx = try ipc.Context.init(allocator);
|
||||
defer ctx.deinit(); // There. Can't leak. Isn't Zig wonderful?
|
||||
|
||||
// SERVER SIDE: creating a service.
|
||||
_ = try ctx.server_init("ipc");
|
||||
|
||||
// signal handler, to quit when asked
|
||||
const S = struct {
|
||||
var should_quit: bool = false;
|
||||
|
||||
fn handler(sig: i32, info: *const os.siginfo_t, _: ?*const anyopaque) callconv(.C) void {
|
||||
print ("A signal has been received: {}\n", .{sig});
|
||||
// Check that we received the correct signal.
|
||||
switch (native_os) {
|
||||
.netbsd => {
|
||||
if (sig != os.SIG.HUP or sig != info.info.signo)
|
||||
return;
|
||||
},
|
||||
else => {
|
||||
if (sig != os.SIG.HUP and sig != info.signo)
|
||||
return;
|
||||
},
|
||||
}
|
||||
should_quit = true;
|
||||
}
|
||||
};
|
||||
|
||||
var sa = os.Sigaction{
|
||||
.handler = .{ .sigaction = &S.handler },
|
||||
.mask = os.empty_sigset, // Do not mask any signal.
|
||||
.flags = os.SA.SIGINFO,
|
||||
};
|
||||
|
||||
// Quit on SIGHUP (kill -1).
|
||||
try os.sigaction(os.SIG.HUP, &sa, null);
|
||||
|
||||
var some_event: ipc.Event = undefined;
|
||||
ctx.timer = 1000; // 1 second
|
||||
var count: u32 = 0;
|
||||
while(! S.should_quit) {
|
||||
some_event = try ctx.wait_event();
|
||||
switch (some_event.t) {
|
||||
.TIMER => {
|
||||
print("\rTimer! ({})", .{count});
|
||||
count += 1;
|
||||
},
|
||||
|
||||
.CONNECTION => {
|
||||
print("New connection: {} so far!\n", .{ctx.pollfd.items.len});
|
||||
},
|
||||
|
||||
.DISCONNECTION => {
|
||||
print("User {} disconnected, {} remainaing.\n"
|
||||
, .{some_event.origin, ctx.pollfd.items.len});
|
||||
},
|
||||
|
||||
.EXTERNAL => {
|
||||
print("Message received from a non IPC socket.\n", .{});
|
||||
print("NOT IMPLEMENTED, YET. It's a suicide, then.\n", .{});
|
||||
break;
|
||||
},
|
||||
|
||||
.SWITCH_RX => {
|
||||
print("Message has been received (SWITCH).\n", .{});
|
||||
print("NOT IMPLEMENTED, YET. It's a suicide, then.\n", .{});
|
||||
break;
|
||||
},
|
||||
|
||||
.SWITCH_TX => {
|
||||
print("Message has been sent (SWITCH).\n", .{});
|
||||
print("NOT IMPLEMENTED, YET. It's a suicide, then.\n", .{});
|
||||
break;
|
||||
},
|
||||
|
||||
.MESSAGE_RX => {
|
||||
print("Client asking for a service through ipcd.\n", .{});
|
||||
defer ctx.close_fd (some_event.origin) catch {};
|
||||
if (some_event.m) |m| {
|
||||
print("{}\n", .{m});
|
||||
defer m.deinit(); // Do not forget to free the message payload.
|
||||
|
||||
// 1. split message
|
||||
var iterator = std.mem.split(u8, m.payload, ";");
|
||||
var service_to_contact = iterator.first();
|
||||
// print("service to contact: {s}\n", .{service_to_contact});
|
||||
var final_destination: ?[]const u8 = null;
|
||||
|
||||
// 2. find relevant part of the message
|
||||
while (iterator.next()) |next| {
|
||||
// print("next part: {s}\n", .{next});
|
||||
var iterator2 = std.mem.split(u8, next, " ");
|
||||
var sname = iterator2.first();
|
||||
var target = iterator2.next();
|
||||
if (target) |t| {
|
||||
// print ("sname: {s} - target: {s}\n", .{sname, t});
|
||||
if (std.mem.eql(u8, service_to_contact, sname)) {
|
||||
final_destination = t;
|
||||
}
|
||||
}
|
||||
else {
|
||||
print("ERROR: no target in: {s}\n", .{next});
|
||||
}
|
||||
}
|
||||
|
||||
// 3. connect whether asked to and send a message
|
||||
if (final_destination) |dest| {
|
||||
print("Connecting to {s} (service requested: {s})\n"
|
||||
, .{dest, service_to_contact});
|
||||
|
||||
var uri = URI.read(dest);
|
||||
|
||||
// 1. in case there is no URI
|
||||
if (std.mem.eql(u8, uri.protocol, dest)) {
|
||||
var newfd = try ctx.connect_service (dest);
|
||||
send_fd (some_event.origin, "ok", newfd);
|
||||
try ctx.close_fd (newfd);
|
||||
}
|
||||
else if (std.mem.eql(u8, uri.protocol, "unix")) {
|
||||
var newfd = try ctx.connect_service (uri.address);
|
||||
send_fd (some_event.origin, "ok", newfd);
|
||||
try ctx.close_fd (newfd);
|
||||
}
|
||||
// 2. else, contact <protocol>d or directly the dest in case there is none.
|
||||
else {
|
||||
var servicefd = try ctx.connect_service (uri.protocol);
|
||||
defer ctx.close_fd (servicefd) catch {};
|
||||
// TODO: make a simple protocol between IPCd and <protocol>d
|
||||
// NEED inform about the connection (success or fail)
|
||||
// FIRST DRAFT:
|
||||
// - IPCd: send a message containing the destination
|
||||
// - PROTOCOLd: send "ok" to inform the connection is established
|
||||
// - PROTOCOLd: send "no" in case there was an error
|
||||
|
||||
var message = try Message.init(servicefd, allocator, dest);
|
||||
defer message.deinit();
|
||||
try ctx.write(message);
|
||||
var response_from_service = try ctx.read_fd(servicefd);
|
||||
if (response_from_service) |r| {
|
||||
defer r.deinit();
|
||||
if (std.mem.eql(u8, r.payload, "ok")) {
|
||||
// OK
|
||||
// print("service has established the connection\n", .{});
|
||||
send_fd (some_event.origin, "ok", servicefd);
|
||||
}
|
||||
else if (std.mem.eql(u8, r.payload, "ne")) {
|
||||
// PROBLEM
|
||||
print("service cannot establish the connection\n", .{});
|
||||
// TODO
|
||||
}
|
||||
else {
|
||||
print("service isn't working properly, its response is: {s}\n", .{r.payload});
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
else {
|
||||
// No message = should be handled as a disconnection.
|
||||
print("No response from service: let's drop everything\n", .{});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// There is a problem: ipcd was contacted without providing
|
||||
// a message, meaning there is nothing to do. This should be
|
||||
// explicitely warned about.
|
||||
var response = try Message.init(some_event.origin
|
||||
, allocator
|
||||
, "lookup message without data");
|
||||
defer response.deinit();
|
||||
try ctx.write(response);
|
||||
}
|
||||
},
|
||||
|
||||
.MESSAGE_TX => {
|
||||
print("Message sent.\n", .{});
|
||||
},
|
||||
|
||||
.ERROR => {
|
||||
print("A problem occured, event: {}, let's suicide\n", .{some_event});
|
||||
break;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
print("Goodbye\n", .{});
|
||||
}
|
||||
|
||||
pub fn main() !u8 {
|
||||
try create_service();
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
const std = @import("std");
|
||||
const net = std.net;
|
||||
const fmt = std.fmt;
|
||||
const os = std.os;
|
||||
|
||||
const print = std.debug.print;
|
||||
|
||||
const ipc = @import("ipc");
|
||||
const hexdump = ipc.hexdump;
|
||||
const Message = ipc.Message;
|
||||
|
||||
pub fn main() !u8 {
|
||||
const config = .{.safety = true};
|
||||
var gpa = std.heap.GeneralPurposeAllocator(config){};
|
||||
defer _ = gpa.deinit();
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
var ctx = try ipc.Context.init(allocator);
|
||||
defer ctx.deinit(); // There. Can't leak. Isn't Zig wonderful?
|
||||
|
||||
// The service to contact, either provided with the SERVICE envvar
|
||||
// or simply using "pong".
|
||||
var should_free_service_to_contact: bool = true;
|
||||
var service_to_contact = std.process.getEnvVarOwned(allocator, "SERVICE") catch blk: {
|
||||
should_free_service_to_contact = false;
|
||||
break :blk "pong";
|
||||
};
|
||||
defer {
|
||||
if (should_free_service_to_contact)
|
||||
allocator.free(service_to_contact);
|
||||
}
|
||||
|
||||
var pongfd = try ctx.connect_ipc(service_to_contact);
|
||||
var message = try Message.init(pongfd, allocator, "bounce me");
|
||||
try ctx.schedule(message);
|
||||
|
||||
var some_event: ipc.Event = undefined;
|
||||
ctx.timer = 2000; // 2 seconds
|
||||
while(true) {
|
||||
some_event = try ctx.wait_event();
|
||||
switch (some_event.t) {
|
||||
.TIMER => {
|
||||
print("Timer!\n", .{});
|
||||
},
|
||||
|
||||
.MESSAGE_RX => {
|
||||
if (some_event.m) |m| {
|
||||
print("message has been bounced: {}\n", .{m});
|
||||
m.deinit();
|
||||
break;
|
||||
}
|
||||
else {
|
||||
print("Received empty message, ERROR.\n", .{});
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
.MESSAGE_TX => {
|
||||
print("Message sent.\n", .{});
|
||||
},
|
||||
|
||||
else => {
|
||||
print("Unexpected event: {}, let's suicide\n", .{some_event});
|
||||
break;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
print("Goodbye\n", .{});
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
const std = @import("std");
|
||||
const os = std.os;
|
||||
|
||||
const ipc = @import("ipc");
|
||||
const hexdump = ipc.hexdump;
|
||||
const Message = ipc.Message;
|
||||
const util = ipc.util;
|
||||
|
||||
// Import send_fd this way in order to produce docs for exchange-fd functions.
|
||||
const exchange_fd = ipc.exchangefd;
|
||||
const send_fd = exchange_fd.send_fd;
|
||||
|
||||
const builtin = @import("builtin");
|
||||
const native_os = builtin.target.os.tag;
|
||||
const print = std.debug.print;
|
||||
const testing = std.testing;
|
||||
|
||||
fn create_service() !void {
|
||||
const config = .{.safety = true};
|
||||
var gpa = std.heap.GeneralPurposeAllocator(config){};
|
||||
defer _ = gpa.deinit();
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
var ctx = try ipc.Context.init(allocator);
|
||||
defer ctx.deinit(); // There. Can't leak. Isn't Zig wonderful?
|
||||
|
||||
// SERVER SIDE: creating a service.
|
||||
_ = try ctx.server_init("pong");
|
||||
|
||||
// signal handler, to quit when asked
|
||||
const S = struct {
|
||||
var should_quit: bool = false;
|
||||
|
||||
fn handler(sig: i32, info: *const os.siginfo_t, _: ?*const anyopaque) callconv(.C) void {
|
||||
print ("A signal has been received: {}\n", .{sig});
|
||||
// Check that we received the correct signal.
|
||||
switch (native_os) {
|
||||
.netbsd => {
|
||||
if (sig != os.SIG.HUP or sig != info.info.signo)
|
||||
return;
|
||||
},
|
||||
else => {
|
||||
if (sig != os.SIG.HUP and sig != info.signo)
|
||||
return;
|
||||
},
|
||||
}
|
||||
should_quit = true;
|
||||
}
|
||||
};
|
||||
|
||||
var sa = os.Sigaction{
|
||||
.handler = .{ .sigaction = &S.handler },
|
||||
.mask = os.empty_sigset, // Do not mask any signal.
|
||||
.flags = os.SA.SIGINFO,
|
||||
};
|
||||
|
||||
// Quit on SIGHUP (kill -1).
|
||||
try os.sigaction(os.SIG.HUP, &sa, null);
|
||||
|
||||
var some_event: ipc.Event = undefined;
|
||||
ctx.timer = 20000; // 2 seconds
|
||||
var count: u32 = 0;
|
||||
while(! S.should_quit) {
|
||||
some_event = try ctx.wait_event();
|
||||
switch (some_event.t) {
|
||||
.TIMER => {
|
||||
print("\rTimer! ({})", .{count});
|
||||
count += 1;
|
||||
},
|
||||
|
||||
.CONNECTION => {
|
||||
print("New connection: {} so far!\n", .{ctx.pollfd.items.len});
|
||||
},
|
||||
|
||||
.DISCONNECTION => {
|
||||
print("User {} disconnected, {} remainaing.\n"
|
||||
, .{some_event.origin, ctx.pollfd.items.len});
|
||||
},
|
||||
|
||||
.MESSAGE_RX => {
|
||||
if (some_event.m) |m| {
|
||||
print("New message ({} bytes)\n", .{m.payload.len});
|
||||
util.print_message ("RECEIVED MESSAGE", m);
|
||||
print("Echoing it...\n", .{});
|
||||
try ctx.schedule(m);
|
||||
}
|
||||
else {
|
||||
print("Error while receiving new message.\n", .{});
|
||||
print("Ignoring...\n", .{});
|
||||
}
|
||||
},
|
||||
|
||||
.MESSAGE_TX => {
|
||||
print("Message sent.\n", .{});
|
||||
},
|
||||
|
||||
else => {
|
||||
print("Error: unexpected event: {}\n", .{some_event});
|
||||
print("Let's suicide.\n", .{});
|
||||
break;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
print("Goodbye\n", .{});
|
||||
}
|
||||
|
||||
pub fn main() !u8 {
|
||||
try create_service();
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,260 @@
|
|||
const std = @import("std");
|
||||
const net = std.net;
|
||||
const fmt = std.fmt;
|
||||
const os = std.os;
|
||||
const testing = std.testing;
|
||||
const print = std.debug.print;
|
||||
|
||||
const ipc = @import("ipc");
|
||||
const hexdump = ipc.hexdump;
|
||||
const Message = ipc.Message;
|
||||
|
||||
// Import send_fd this way in order to produce docs for exchange-fd functions.
|
||||
const exchange_fd = ipc.exchangefd;
|
||||
const send_fd = exchange_fd.send_fd;
|
||||
|
||||
const builtin = @import("builtin");
|
||||
const native_os = builtin.target.os.tag;
|
||||
const print_eq = ipc.util.print_eq;
|
||||
const URI = ipc.util.URI;
|
||||
|
||||
fn init_tcp_server(allocator: std.mem.Allocator, server: *net.StreamServer) !i32 {
|
||||
var address = std.process.getEnvVarOwned(allocator, "ADDRESS") catch |err| switch(err) {
|
||||
error.EnvironmentVariableNotFound => blk: {
|
||||
print ("no ADDRESS envvar: TCPd will listen on 127.0.0.1:9000\n", .{});
|
||||
break :blk try allocator.dupe(u8, "127.0.0.1:9000");
|
||||
},
|
||||
else => { return err; },
|
||||
};
|
||||
defer allocator.free(address);
|
||||
|
||||
var iterator = std.mem.split(u8, address, ":");
|
||||
var real_tcp_address = iterator.first();
|
||||
var real_tcp_port = try std.fmt.parseUnsigned(u16, iterator.rest(), 10);
|
||||
|
||||
print ("TCP address [{s}] port [{}]\n", .{real_tcp_address, real_tcp_port});
|
||||
|
||||
server.* = net.StreamServer.init(.{.reuse_address = true});
|
||||
var socket_addr = try net.Address.parseIp(real_tcp_address, real_tcp_port);
|
||||
try server.listen(socket_addr);
|
||||
|
||||
const newfd = server.sockfd orelse return error.SocketLOL; // TODO
|
||||
return newfd;
|
||||
}
|
||||
|
||||
fn create_service() !void {
|
||||
const config = .{.safety = true};
|
||||
var gpa = std.heap.GeneralPurposeAllocator(config){};
|
||||
defer _ = gpa.deinit();
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
var ctx = try ipc.Context.init(allocator);
|
||||
defer ctx.deinit(); // There. Can't leak. Isn't Zig wonderful?
|
||||
|
||||
// SERVER SIDE: creating a service.
|
||||
var service_name = std.process.getEnvVarOwned(allocator, "IPC_SERVICE_NAME") catch |err| switch(err) {
|
||||
error.EnvironmentVariableNotFound => blk: {
|
||||
print ("no IPC_SERVICE_NAME envvar: TCPd will be named 'tcp'\n", .{});
|
||||
break :blk try allocator.dupe(u8, "tcp");
|
||||
},
|
||||
else => { return err; },
|
||||
};
|
||||
defer allocator.free(service_name);
|
||||
|
||||
_ = try ctx.server_init(service_name);
|
||||
|
||||
// signal handler, to quit when asked
|
||||
const S = struct {
|
||||
var should_quit: bool = false;
|
||||
|
||||
fn handler(sig: i32, info: *const os.siginfo_t, _: ?*const anyopaque) callconv(.C) void {
|
||||
print ("A signal has been received: {}\n", .{sig});
|
||||
// Check that we received the correct signal.
|
||||
switch (native_os) {
|
||||
.netbsd => {
|
||||
if (sig != os.SIG.HUP or sig != info.info.signo)
|
||||
return;
|
||||
},
|
||||
else => {
|
||||
if (sig != os.SIG.HUP and sig != info.signo)
|
||||
return;
|
||||
},
|
||||
}
|
||||
should_quit = true;
|
||||
}
|
||||
};
|
||||
|
||||
var sa = os.Sigaction{
|
||||
.handler = .{ .sigaction = &S.handler },
|
||||
.mask = os.empty_sigset, // Do not mask any signal.
|
||||
.flags = os.SA.SIGINFO,
|
||||
};
|
||||
|
||||
// Quit on SIGHUP (kill -1).
|
||||
try os.sigaction(os.SIG.HUP, &sa, null);
|
||||
|
||||
var server: net.StreamServer = undefined;
|
||||
var serverfd = try init_tcp_server(allocator, &server);
|
||||
try ctx.add_external (serverfd);
|
||||
|
||||
var some_event: ipc.Event = undefined;
|
||||
var previous_event: ipc.Event.Type = ipc.Event.Type.ERROR;
|
||||
ctx.timer = 1000; // 1 second
|
||||
var count: u32 = 0;
|
||||
while(! S.should_quit) {
|
||||
some_event = try ctx.wait_event();
|
||||
|
||||
// For clarity in the output.
|
||||
if (some_event.t != .TIMER and previous_event == .TIMER ) { print("\n", .{}); }
|
||||
previous_event = some_event.t;
|
||||
|
||||
switch (some_event.t) {
|
||||
.TIMER => {
|
||||
print ("\rTimer! ({})", .{count});
|
||||
count += 1;
|
||||
},
|
||||
|
||||
.CONNECTION => {
|
||||
print ("New connection: {} so far!\n", .{ctx.pollfd.items.len});
|
||||
},
|
||||
|
||||
.DISCONNECTION => {
|
||||
print ("User {} disconnected, {} remaining.\n"
|
||||
, .{some_event.origin, ctx.pollfd.items.len});
|
||||
},
|
||||
|
||||
.EXTERNAL => {
|
||||
print ("Message received from a non IPC socket.\n", .{});
|
||||
var client = try server.accept(); // net.StreamServer.Connection
|
||||
errdefer client.stream.close();
|
||||
// Receiving a new client from the EXTERNAL socket.
|
||||
// New client = new switch from a distant TCP connection to a
|
||||
// local libipc service.
|
||||
|
||||
var buffer: [10000]u8 = undefined;
|
||||
var size = try client.stream.read(&buffer);
|
||||
var service_to_contact = buffer[0..size];
|
||||
|
||||
if (service_to_contact.len == 0) {
|
||||
print("Error, no service provided, closing the connection.\n", .{});
|
||||
client.stream.close();
|
||||
continue;
|
||||
}
|
||||
|
||||
print ("Ask to connect to service [{s}].\n", .{service_to_contact});
|
||||
var servicefd = ctx.connect_service (service_to_contact) catch |err| {
|
||||
print("Error while connecting to the service {s}: {}.\n"
|
||||
, .{service_to_contact, err});
|
||||
print ("Closing the connection.\n", .{});
|
||||
client.stream.close();
|
||||
continue;
|
||||
};
|
||||
errdefer ctx.close_fd (servicefd) catch {};
|
||||
|
||||
print ("Send a message to inform remote TCPd that the connection is established.\n", .{});
|
||||
_ = try client.stream.write("ok");
|
||||
|
||||
print ("Add current client as external connection (for now).\n", .{});
|
||||
try ctx.add_external (client.stream.handle);
|
||||
|
||||
print ("Message sent, switching.\n", .{});
|
||||
try ctx.add_switch(client.stream.handle, servicefd);
|
||||
|
||||
print ("DONE.\n", .{});
|
||||
|
||||
// Some protocols will require to change the default functions
|
||||
// to read and to write on the client socket.
|
||||
// Function to call: ctx.set_switch_callbacks(clientfd, infn, outfn);
|
||||
},
|
||||
|
||||
.SWITCH_RX => {
|
||||
print ("Message has been received (SWITCH fd {}).\n", .{some_event.origin});
|
||||
// if (some_event.m) |m| {
|
||||
// var hexbuf: [4000]u8 = undefined;
|
||||
// var hexfbs = std.io.fixedBufferStream(&hexbuf);
|
||||
// var hexwriter = hexfbs.writer();
|
||||
// try hexdump.hexdump(hexwriter, "Received", m.payload);
|
||||
// print("{s}\n", .{hexfbs.getWritten()});
|
||||
// }
|
||||
// else {
|
||||
// print ("Message received without actually a message?! {}", .{some_event});
|
||||
// }
|
||||
},
|
||||
|
||||
.SWITCH_TX => {
|
||||
print ("Message has been sent (SWITCH fd {}).\n", .{some_event.origin});
|
||||
},
|
||||
|
||||
.MESSAGE_RX => {
|
||||
print ("Client asking for a service through TCPd.\n", .{});
|
||||
errdefer ctx.close (some_event.index) catch {};
|
||||
if (some_event.m) |m| {
|
||||
defer m.deinit(); // Do not forget to free the message payload.
|
||||
|
||||
print ("URI to contact {s}\n", .{m.payload});
|
||||
var uri = URI.read(m.payload);
|
||||
print ("proto [{s}] address [{s}] path [{s}]\n"
|
||||
, .{uri.protocol, uri.address, uri.path});
|
||||
|
||||
var iterator = std.mem.split(u8, uri.address, ":");
|
||||
var real_tcp_address = iterator.first();
|
||||
var real_tcp_port = try std.fmt.parseUnsigned(u16, iterator.rest(), 10);
|
||||
|
||||
var socket_addr = try net.Address.parseIp(real_tcp_address, real_tcp_port);
|
||||
var stream = try net.tcpConnectToAddress(socket_addr);
|
||||
errdefer stream.close();
|
||||
|
||||
print ("Writing URI PATH: {s}\n", .{uri.path});
|
||||
_ = try stream.write(uri.path);
|
||||
|
||||
print ("Writing URI PATH - written, waiting for the final 'ok'.\n", .{});
|
||||
var buffer: [10000]u8 = undefined;
|
||||
var size = try stream.read(&buffer);
|
||||
if (! std.mem.eql(u8, buffer[0..size], "ok")) {
|
||||
print ("didn't receive 'ok', let's kill the connection\n", .{});
|
||||
stream.close();
|
||||
try ctx.close(some_event.index);
|
||||
continue;
|
||||
}
|
||||
print ("Final 'ok' received, sending 'ok' to IPCd.\n", .{});
|
||||
|
||||
// Connection is established, inform IPCd.
|
||||
var response = try Message.init(some_event.origin, allocator, "ok");
|
||||
defer response.deinit();
|
||||
try ctx.write(response);
|
||||
|
||||
print ("Add current client as external connection (for now).\n", .{});
|
||||
try ctx.add_external (stream.handle);
|
||||
|
||||
print ("Finally, add switching\n", .{});
|
||||
try ctx.add_switch(some_event.origin, stream.handle);
|
||||
// Could invoke ctx.set_switch_callbacks but TCP sockets are
|
||||
// working pretty well with default functions.
|
||||
}
|
||||
else {
|
||||
// TCPd was contacted without providing a message, nothing to do.
|
||||
var response = try Message.init(some_event.origin, allocator, "no");
|
||||
defer response.deinit();
|
||||
try ctx.write(response);
|
||||
try ctx.close(some_event.index);
|
||||
}
|
||||
},
|
||||
|
||||
.MESSAGE_TX => {
|
||||
print ("Message sent.\n", .{});
|
||||
},
|
||||
|
||||
.ERROR => {
|
||||
print ("A problem occured, event: {}, let's suicide.\n", .{some_event});
|
||||
break;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
print ("Goodbye\n", .{});
|
||||
}
|
||||
|
||||
pub fn main() !u8 {
|
||||
try create_service();
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
const std = @import("std");
|
||||
|
||||
const VERSION = "0.1.0";
|
||||
|
||||
// Although this function looks imperative, note that its job is to
|
||||
// declaratively construct a build graph that will be executed by an external
|
||||
// runner.
|
||||
pub fn build(b: *std.Build) void {
|
||||
const is_release = b.option(bool, "release", "Compile a release build.") orelse false;
|
||||
|
||||
if (is_release) {
|
||||
std.log.err("hello, this is the release stuff", .{});
|
||||
}
|
||||
// Standard target options allows the person running `zig build` to choose
|
||||
// what target to build for. Here we do not override the defaults, which
|
||||
// means any target is allowed, and the default is native. Other options
|
||||
// for restricting supported target set are available.
|
||||
const target = b.standardTargetOptions(.{});
|
||||
|
||||
// Standard optimization options allow the person running `zig build` to select
|
||||
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
|
||||
// set a preferred release mode, allowing the user to decide how to optimize.
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
const static_lib = b.addStaticLibrary(.{
|
||||
.name = "ipc",
|
||||
// In this case the main source file is merely a path, however, in more
|
||||
// complicated build scripts, this could be a generated file.
|
||||
.root_source_file = .{ .path = "src/bindings.zig" },
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
// Link with the libc of the target system since the C allocator
|
||||
// is required in the bindings.
|
||||
static_lib.linkLibC();
|
||||
|
||||
// This declares intent for the library to be installed into the standard
|
||||
// location when the user invokes the "install" step (the default step when
|
||||
// running `zig build`).
|
||||
static_lib.install();
|
||||
|
||||
const shared_lib = b.addSharedLibrary(.{
|
||||
.name = "ipc",
|
||||
.root_source_file = .{ .path = "src/bindings.zig" },
|
||||
.version = comptime (try std.builtin.Version.parse(VERSION)),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
shared_lib.linkLibC();
|
||||
shared_lib.install();
|
||||
|
||||
// Creates a step for unit testing.
|
||||
const main_tests = b.addTest(.{
|
||||
.root_source_file = .{ .path = "src/main.zig" },
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
main_tests.linkLibC();
|
||||
|
||||
// This creates a build step. It will be visible in the `zig build --help` menu,
|
||||
// and can be selected like this: `zig build test`
|
||||
// This will evaluate the `test` step rather than the default, which is "install".
|
||||
const test_step = b.step("test", "Run library tests");
|
||||
test_step.dependOn(&main_tests.step);
|
||||
|
||||
const install_static_lib = b.addInstallArtifact(static_lib);
|
||||
const static_lib_step = b.step("static", "Compile LibIPC as a static library.");
|
||||
static_lib_step.dependOn(&install_static_lib.step);
|
||||
|
||||
const install_shared_lib = b.addInstallArtifact(shared_lib);
|
||||
// b.getInstallStep().dependOn(&install_shared_lib.step);
|
||||
const shared_lib_step = b.step("shared", "Compile LibIPC as a shared library.");
|
||||
shared_lib_step.dependOn(&install_shared_lib.step);
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
/docs/
|
||||
/lib/
|
||||
/bin/
|
||||
/.shards/
|
||||
*.dwarf
|
||||
old
|
||||
shard.lock
|
||||
*.log
|
|
@ -0,0 +1,231 @@
|
|||
require "option_parser"
|
||||
|
||||
require "../src/main.cr"
|
||||
require "yaml"
|
||||
|
||||
require "baguette-crystal-base"
|
||||
|
||||
require "../authd/libauth.cr"
|
||||
|
||||
# require "./altideal-client.cr"
|
||||
# require "./yaml_uuid.cr" # YAML UUID parser
|
||||
# require "./authd_api.cr" # Authd interface functions
|
||||
|
||||
|
||||
class Context
|
||||
class_property simulation = false # do not perform the action
|
||||
|
||||
class_property authd_login = "undef" # undef authd user
|
||||
class_property authd_pass = "undef" # undef authd user password
|
||||
class_property shared_key = "undef" # undef authd user password
|
||||
|
||||
# # Properties to select what to display when printing a deal.
|
||||
# class_property print_title = true
|
||||
# class_property print_description = true
|
||||
# class_property print_owner = true
|
||||
# class_property print_nb_comments = true
|
||||
|
||||
class_property command = "not-implemented"
|
||||
|
||||
class_property user_profile : Hash(String,JSON::Any)?
|
||||
class_property phone : String?
|
||||
class_property email : String?
|
||||
|
||||
# Will be parsed later, with a specific parser.
|
||||
class_property args : Array(String)? = nil
|
||||
end
|
||||
|
||||
# require "./parse-me"
|
||||
require "./better-parser"
|
||||
|
||||
class Actions
|
||||
|
||||
def self.ask_password
|
||||
STDOUT << "password: "
|
||||
STDOUT << `stty -echo`
|
||||
STDOUT.flush
|
||||
password = STDIN.gets.try &.chomp
|
||||
|
||||
STDOUT << '\n'
|
||||
STDOUT << `stty echo`
|
||||
|
||||
password
|
||||
end
|
||||
|
||||
def self.ask_something(str : String) : String?
|
||||
STDOUT << "#{str} "
|
||||
STDOUT.flush
|
||||
answer = STDIN.gets.try &.chomp
|
||||
answer
|
||||
end
|
||||
|
||||
|
||||
property the_call = {} of String => Proc(Nil)
|
||||
property authd : AuthD::Client
|
||||
|
||||
def initialize(@authd)
|
||||
@the_call["user-add"] = ->user_add
|
||||
@the_call["user-mod"] = ->user_mod
|
||||
@the_call["user-registration"] = ->user_registration # Do not require admin priviledges.
|
||||
@the_call["user-delete"] = ->user_deletion # Do not require admin priviledges.
|
||||
@the_call["user-get"] = ->user_get # Do not require authentication.
|
||||
@the_call["user-validation"] = ->user_validation # Do not require authentication.
|
||||
@the_call["user-recovery"] = ->user_recovery # Do not require authentication.
|
||||
@the_call["user-search"] = ->user_search # Do not require authentication.
|
||||
|
||||
@the_call["permission-set"] = ->permission_set
|
||||
@the_call["permission-check"] = ->permission_check
|
||||
|
||||
end
|
||||
|
||||
#
|
||||
# For all functions: the number of arguments is already tested.
|
||||
#
|
||||
|
||||
def user_add
|
||||
puts "User add!!!"
|
||||
args = Context.args.not_nil!
|
||||
login, email, phone = args[0..2]
|
||||
profile = Context.user_profile
|
||||
|
||||
password = Actions.ask_password
|
||||
exit 1 unless password
|
||||
|
||||
pp! authd.add_user login, password.not_nil!, email, phone, profile: profile
|
||||
rescue e : AuthD::Exception
|
||||
puts "error: #{e.message}"
|
||||
end
|
||||
|
||||
def user_registration
|
||||
args = Context.args.not_nil!
|
||||
login, email, phone = args[0..2]
|
||||
profile = Context.user_profile
|
||||
|
||||
password = Actions.ask_password
|
||||
exit 1 unless password
|
||||
|
||||
res = authd.register login, password.not_nil!, email, phone, profile: profile
|
||||
puts res
|
||||
rescue e
|
||||
puts "error: #{e.message}"
|
||||
end
|
||||
|
||||
# TODO
|
||||
def user_mod
|
||||
args = Context.args.not_nil!
|
||||
userid = args[0]
|
||||
|
||||
password : String? = nil
|
||||
|
||||
should_ask_password = Actions.ask_something "Should we change the password (Yn) ?" || "n"
|
||||
case should_ask_password
|
||||
when /y/i
|
||||
Baguette::Log.debug "Ok let's change the password!"
|
||||
password = Actions.ask_password
|
||||
exit 1 unless password
|
||||
else
|
||||
Baguette::Log.debug "Ok no change in password."
|
||||
end
|
||||
|
||||
email = Context.email
|
||||
phone = Context.phone
|
||||
|
||||
Baguette::Log.error "This function shouldn't be used for now."
|
||||
Baguette::Log.error "It is way too cumbersome."
|
||||
|
||||
# res = authd.add_user login, password, email, phone, profile: profile
|
||||
# puts res
|
||||
end
|
||||
|
||||
def user_deletion
|
||||
args = Context.args.not_nil!
|
||||
userid = args[0].to_i
|
||||
|
||||
# Check if the request comes from an admin or the user.
|
||||
res = if Context.shared_key.nil?
|
||||
authd.delete userid, Context.authd_login, Context.authd_pass
|
||||
else
|
||||
authd.delete userid, Context.shared_key
|
||||
end
|
||||
|
||||
puts res
|
||||
end
|
||||
|
||||
def user_validation
|
||||
args = Context.args.not_nil!
|
||||
login, activation_key = args[0..1]
|
||||
pp! authd.validate_user login, activation_key
|
||||
end
|
||||
def user_search
|
||||
args = Context.args.not_nil!
|
||||
login = args[0]
|
||||
pp! authd.search_user login
|
||||
end
|
||||
def user_get
|
||||
args = Context.args.not_nil!
|
||||
login = args[0]
|
||||
pp! authd.get_user? login
|
||||
end
|
||||
def user_recovery
|
||||
args = Context.args.not_nil!
|
||||
login, email = args[0..1]
|
||||
pp! authd.ask_password_recovery login, email
|
||||
end
|
||||
|
||||
def permission_check
|
||||
args = Context.args.not_nil!
|
||||
user, application, resource = args[0..2]
|
||||
# pp! user, application, resource
|
||||
|
||||
res = @authd.check_permission user.to_i, application, resource
|
||||
puts res
|
||||
end
|
||||
|
||||
def permission_set
|
||||
args = Context.args.not_nil!
|
||||
user, application, resource, permission = args[0..3]
|
||||
# pp! user, application, resource, permission
|
||||
|
||||
perm = AuthD::User::PermissionLevel.parse(permission)
|
||||
res = @authd.set_permission user.to_i, application, resource, perm
|
||||
puts res
|
||||
end
|
||||
end
|
||||
|
||||
def main
|
||||
|
||||
# Authd connection.
|
||||
authd = AuthD::Client.new
|
||||
authd.key = Context.shared_key if Context.shared_key != "undef"
|
||||
|
||||
# Authd token.
|
||||
# FIXME: not sure about getting the token, it seems not used elsewhere.
|
||||
# If login == pass == "undef": do not even try.
|
||||
#unless Context.authd_login == Context.authd_pass && Context.authd_login == "undef"
|
||||
# login = Context.authd_login
|
||||
# pass = Context.authd_pass
|
||||
# token = authd.get_token? login, pass
|
||||
# raise "cannot get a token" if token.nil?
|
||||
# # authd.login token
|
||||
#end
|
||||
|
||||
actions = Actions.new authd
|
||||
|
||||
# Now we did read the intent, we should proceed doing what was asked.
|
||||
begin
|
||||
actions.the_call[Context.command].call
|
||||
rescue e
|
||||
Baguette::Log.info "The command is not recognized (or implemented)."
|
||||
end
|
||||
|
||||
# authd disconnection
|
||||
authd.close
|
||||
rescue e
|
||||
Baguette::Log.info "Exception: #{e}"
|
||||
end
|
||||
|
||||
|
||||
# Command line:
|
||||
# tool [options] command [options-for-command]
|
||||
|
||||
main
|
|
@ -0,0 +1,230 @@
|
|||
require "option_parser"
|
||||
|
||||
opt_authd_admin = -> (parser : OptionParser) {
|
||||
parser.on "-k file", "--key-file file", "Read the authd shared key from a file." do |file|
|
||||
Context.shared_key = File.read(file).chomp
|
||||
Baguette::Log.info "Key for admin operations: #{Context.shared_key}."
|
||||
end
|
||||
}
|
||||
|
||||
# frequently used functions
|
||||
opt_authd_login = -> (parser : OptionParser) {
|
||||
parser.on "-l LOGIN", "--login LOGIN", "Authd user login." do |login|
|
||||
Context.authd_login = login
|
||||
Baguette::Log.info "User login for authd: #{Context.authd_login}."
|
||||
end
|
||||
parser.on "-p PASSWORD", "--password PASSWORD", "Authd user password." do |password|
|
||||
Context.authd_pass = password
|
||||
Baguette::Log.info "User password for authd: #{Context.authd_pass}."
|
||||
end
|
||||
}
|
||||
|
||||
opt_help = -> (parser : OptionParser) {
|
||||
parser.on "help", "Prints this help message." do
|
||||
puts parser
|
||||
exit 0
|
||||
end
|
||||
}
|
||||
|
||||
opt_profile = -> (parser : OptionParser) {
|
||||
parser.on "-P file", "--profile file", "Read the user profile from a file." do |file|
|
||||
Context.user_profile = JSON.parse(File.read file).as_h
|
||||
Baguette::Log.info "Reading the user profile: #{Context.user_profile}."
|
||||
end
|
||||
}
|
||||
|
||||
opt_phone = -> (parser : OptionParser) {
|
||||
parser.on "-n phone", "--phone-number num", "Phone number." do |phone|
|
||||
Context.phone = phone
|
||||
Baguette::Log.info "Reading the user phone number: #{Context.phone}."
|
||||
end
|
||||
}
|
||||
|
||||
opt_email = -> (parser : OptionParser) {
|
||||
parser.on "-e email", "--email address", "Email address." do |email|
|
||||
Context.email = email
|
||||
Baguette::Log.info "Reading the user email address: #{Context.email}."
|
||||
end
|
||||
}
|
||||
|
||||
|
||||
# Unrecognized parameters are used to create commands with multiple arguments.
|
||||
# Example: user add _login email phone_
|
||||
# Here, login, email and phone are unrecognized arguments.
|
||||
# Still, the "user add" command expect them.
|
||||
unrecognized_args_to_context_args = -> (parser : OptionParser, n_expected_args : Int32) {
|
||||
# With the right args, these will be interpreted as serialized data.
|
||||
parser.unknown_args do |args|
|
||||
if args.size != n_expected_args
|
||||
Baguette::Log.error "expected number of arguments: #{n_expected_args}, received: #{args.size}"
|
||||
Baguette::Log.error "args: #{args}"
|
||||
Baguette::Log.error "#{parser}"
|
||||
exit 1
|
||||
end
|
||||
args.each do |arg|
|
||||
Baguette::Log.debug "Unrecognized argument: #{arg} (adding to Context.args)"
|
||||
if Context.args.nil?
|
||||
Context.args = Array(String).new
|
||||
end
|
||||
Context.args.not_nil! << arg
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
parser = OptionParser.new do |parser|
|
||||
parser.banner = "usage: #{PROGRAM_NAME} command help"
|
||||
parser.on "-v verbosity", "--verbosity v", "Verbosity. From 0 to 4 (debug)." do |v|
|
||||
Baguette::Context.verbosity = v.to_i
|
||||
Baguette::Log.info "verbosity = #{v}"
|
||||
end
|
||||
parser.on "-h", "--help", "Prints this help message." do
|
||||
puts "usage: #{PROGRAM_NAME} command help"
|
||||
puts parser
|
||||
exit 0
|
||||
end
|
||||
|
||||
parser.on "user", "Operations on users." do
|
||||
parser.banner = "Usage: user [add | mod | delete | validate | search | get | recover | register ]"
|
||||
|
||||
parser.on "add", "Adding a user to the DB." do
|
||||
parser.banner = "usage: user add login email phone [-P profile] [opt]"
|
||||
Baguette::Log.info "Adding a user to the DB."
|
||||
Context.command = "user-add"
|
||||
opt_authd_admin.call parser
|
||||
opt_profile.call parser
|
||||
opt_email.call parser
|
||||
opt_phone.call parser
|
||||
opt_help.call parser
|
||||
# login email phone
|
||||
unrecognized_args_to_context_args.call parser, 3
|
||||
end
|
||||
|
||||
parser.on "mod", "Modify a user account." do
|
||||
parser.banner = "Usage: user mod userid [-e email|-n phone|-P profile] [opt]"
|
||||
Baguette::Log.info "Modify a user account."
|
||||
Context.command = "user-mod"
|
||||
opt_authd_admin.call parser
|
||||
opt_email.call parser
|
||||
opt_phone.call parser
|
||||
opt_profile.call parser
|
||||
opt_help.call parser
|
||||
# userid
|
||||
unrecognized_args_to_context_args.call parser, 1
|
||||
end
|
||||
|
||||
parser.on "delete", "Remove user." do
|
||||
parser.banner = "Usage: user delete userid [opt]"
|
||||
Baguette::Log.info "Remove user."
|
||||
Context.command = "user-delete"
|
||||
# You can either be the owner of the account, or an admin.
|
||||
opt_authd_login.call parser
|
||||
opt_authd_admin.call parser
|
||||
opt_help.call parser
|
||||
# userid
|
||||
unrecognized_args_to_context_args.call parser, 1
|
||||
end
|
||||
|
||||
parser.on "validate", "Validate user." do
|
||||
parser.banner = "Usage: user validate login activation-key [opt]"
|
||||
Baguette::Log.info "Validate user."
|
||||
Context.command = "user-validate"
|
||||
# No need to be authenticated.
|
||||
opt_help.call parser
|
||||
# login activation-key
|
||||
unrecognized_args_to_context_args.call parser, 2
|
||||
end
|
||||
|
||||
parser.on "get", "Get user info." do
|
||||
parser.banner = "Usage: user get login [opt]"
|
||||
Baguette::Log.info "Get user info."
|
||||
Context.command = "user-get"
|
||||
# No need to be authenticated.
|
||||
opt_help.call parser
|
||||
# login
|
||||
unrecognized_args_to_context_args.call parser, 1
|
||||
end
|
||||
|
||||
parser.on "search", "Search user." do
|
||||
parser.banner = "Usage: user recover login [opt]"
|
||||
Baguette::Log.info "Search user."
|
||||
Context.command = "user-search"
|
||||
# No need to be authenticated.
|
||||
opt_help.call parser
|
||||
# login
|
||||
unrecognized_args_to_context_args.call parser, 1
|
||||
end
|
||||
|
||||
parser.on "recover", "Recover user password." do
|
||||
parser.banner = "Usage: user recover login email [opt]"
|
||||
Baguette::Log.info "Recover user password."
|
||||
Context.command = "user-recovery"
|
||||
# No need to be authenticated.
|
||||
opt_help.call parser
|
||||
# login email
|
||||
unrecognized_args_to_context_args.call parser, 2
|
||||
end
|
||||
|
||||
|
||||
# Do not require to be admin.
|
||||
parser.on "register", "Register a user (requires activation)." do
|
||||
parser.banner = "Usage: user register login email phone [-P profile] [opt]"
|
||||
Baguette::Log.info "Register a user (requires activation)."
|
||||
Context.command = "user-registration"
|
||||
# These options shouldn't be used here,
|
||||
# email and phone parameters are mandatory.
|
||||
# opt_email.call parser
|
||||
# opt_phone.call parser
|
||||
opt_profile.call parser
|
||||
opt_help.call parser
|
||||
# login email phone
|
||||
unrecognized_args_to_context_args.call parser, 3
|
||||
end
|
||||
end
|
||||
|
||||
parser.on "permission", "Permissions management." do
|
||||
parser.banner = "Usage: permissions [check | set]"
|
||||
parser.on "set", "Set permissions." do
|
||||
parser.banner = <<-END
|
||||
usage: permission set user application resource permission
|
||||
example: permission set 1002 my-application chat read
|
||||
|
||||
permission list: none read edit admin
|
||||
END
|
||||
Baguette::Log.info "Set permissions."
|
||||
Context.command = "permission-set"
|
||||
opt_authd_admin.call parser
|
||||
opt_help.call parser
|
||||
# userid application resource permission
|
||||
unrecognized_args_to_context_args.call parser, 4
|
||||
end
|
||||
|
||||
parser.on "check", "Check permissions." do
|
||||
parser.banner = <<-END
|
||||
usage: permission check user application resource
|
||||
example: permission check 1002 my-application chat
|
||||
|
||||
permission list: none read edit admin
|
||||
END
|
||||
Baguette::Log.info "Check permissions."
|
||||
Context.command = "permission-check"
|
||||
opt_authd_admin.call parser
|
||||
opt_help.call parser
|
||||
# userid application resource
|
||||
unrecognized_args_to_context_args.call parser, 3
|
||||
end
|
||||
end
|
||||
|
||||
parser.unknown_args do |args|
|
||||
if args.size > 0
|
||||
Baguette::Log.warning "Unknown args: #{args}"
|
||||
end
|
||||
end
|
||||
|
||||
# parser.on "-X user-password", "--user-password pass", "Read the new user password." do |pass|
|
||||
# password = pass
|
||||
# end
|
||||
|
||||
end
|
||||
|
||||
|
||||
parser.parse
|
|
@ -0,0 +1,239 @@
|
|||
extend AuthD
|
||||
|
||||
class Baguette::Configuration
|
||||
class Auth < IPC
|
||||
property recreate_indexes : Bool = false
|
||||
property storage : String = "storage"
|
||||
property registrations : Bool = false
|
||||
property require_email : Bool = false
|
||||
property activation_template : String = "email-activation"
|
||||
property recovery_template : String = "email-recovery"
|
||||
property mailer_exe : String = "mailer"
|
||||
property read_only_profile_keys : Array(String) = Array(String).new
|
||||
|
||||
property print_password_recovery_parameters : Bool = false
|
||||
end
|
||||
end
|
||||
|
||||
# Provides a JWT-based authentication scheme for service-specific users.
|
||||
class AuthD::Service < IPC
|
||||
property configuration : Baguette::Configuration::Auth
|
||||
|
||||
# DB and its indexes.
|
||||
property users : DODB::DataBase(User)
|
||||
property users_per_uid : DODB::Index(User)
|
||||
property users_per_login : DODB::Index(User)
|
||||
|
||||
# #{@configuration.storage}/last_used_uid
|
||||
property last_uid_file : String
|
||||
|
||||
def initialize(@configuration)
|
||||
super()
|
||||
|
||||
@users = DODB::DataBase(User).new @configuration.storage
|
||||
@users_per_uid = @users.new_index "uid", &.uid.to_s
|
||||
@users_per_login = @users.new_index "login", &.login
|
||||
|
||||
@last_uid_file = "#{@configuration.storage}/last_used_uid"
|
||||
|
||||
if @configuration.recreate_indexes
|
||||
@users.reindex_everything!
|
||||
end
|
||||
|
||||
self.timer @configuration.ipc_timer
|
||||
self.service_init "auth"
|
||||
end
|
||||
|
||||
def hash_password(password : String) : String
|
||||
digest = OpenSSL::Digest.new "sha256"
|
||||
digest << password
|
||||
digest.hexfinal
|
||||
end
|
||||
|
||||
# new_uid reads the last given UID and returns it incremented.
|
||||
# Splitting the retrieval and record of new user ids allows to
|
||||
# only increment when an user fully registers, thus avoiding a
|
||||
# Denial of Service attack.
|
||||
#
|
||||
# WARNING: to record this new UID, new_uid_commit must be called.
|
||||
# WARNING: new_uid isn't thread safe.
|
||||
def new_uid
|
||||
begin
|
||||
uid = File.read(@last_uid_file).to_i
|
||||
rescue
|
||||
uid = 999
|
||||
end
|
||||
|
||||
uid += 1
|
||||
end
|
||||
|
||||
# new_uid_commit records the new UID.
|
||||
# WARNING: new_uid_commit isn't thread safe.
|
||||
def new_uid_commit(uid : Int)
|
||||
File.write @last_uid_file, uid.to_s
|
||||
end
|
||||
|
||||
def handle_request(event : IPC::Event)
|
||||
request_start = Time.utc
|
||||
|
||||
array = event.message.not_nil!
|
||||
slice = Slice.new array.to_unsafe, array.size
|
||||
message = IPCMessage::TypedMessage.deserialize slice
|
||||
request = AuthD.requests.parse_ipc_json message.not_nil!
|
||||
|
||||
if request.nil?
|
||||
raise "unknown request type"
|
||||
end
|
||||
|
||||
request_name = request.class.name.sub /^AuthD::Request::/, ""
|
||||
Baguette::Log.debug "<< #{request_name}"
|
||||
|
||||
response = begin
|
||||
request.handle self
|
||||
rescue e : UserNotFound
|
||||
Baguette::Log.error "#{request_name} user not found"
|
||||
AuthD::Response::Error.new "authorization error"
|
||||
rescue e : AuthenticationInfoLacking
|
||||
Baguette::Log.error "#{request_name} lacking authentication info"
|
||||
AuthD::Response::Error.new "authorization error"
|
||||
rescue e : AdminAuthorizationException
|
||||
Baguette::Log.error "#{request_name} admin authentication failed"
|
||||
AuthD::Response::Error.new "authorization error"
|
||||
rescue e
|
||||
Baguette::Log.error "#{request_name} generic error #{e}"
|
||||
AuthD::Response::Error.new "unknown error"
|
||||
end
|
||||
|
||||
# If clients sent requests with an “id” field, it is copied
|
||||
# in the responses. Allows identifying responses easily.
|
||||
response.id = request.id
|
||||
|
||||
schedule event.fd, response
|
||||
|
||||
duration = Time.utc - request_start
|
||||
|
||||
response_name = response.class.name.sub /^AuthD::Response::/, ""
|
||||
|
||||
if response.is_a? AuthD::Response::Error
|
||||
Baguette::Log.warning ">> #{response_name} (#{response.reason})"
|
||||
else
|
||||
Baguette::Log.debug ">> #{response_name} (Total duration: #{duration})"
|
||||
end
|
||||
end
|
||||
|
||||
def get_user_from_token(token : String)
|
||||
token_payload = Token.from_s(@configuration.shared_key, token)
|
||||
|
||||
@users_per_uid.get? token_payload.uid.to_s
|
||||
end
|
||||
|
||||
def run
|
||||
Baguette::Log.title "Starting authd"
|
||||
|
||||
Baguette::Log.info "(mailer) Email activation template: #{@configuration.activation_template}"
|
||||
Baguette::Log.info "(mailer) Email recovery template: #{@configuration.recovery_template}"
|
||||
|
||||
self.loop do |event|
|
||||
case event.type
|
||||
when LibIPC::EventType::Timer
|
||||
Baguette::Log.debug "Timer" if @configuration.print_ipc_timer
|
||||
|
||||
when LibIPC::EventType::MessageRx
|
||||
Baguette::Log.debug "Received message from #{event.fd}" if @configuration.print_ipc_message_received
|
||||
begin
|
||||
handle_request event
|
||||
rescue e
|
||||
Baguette::Log.error "#{e.message}"
|
||||
# send event.fd, Response::Error.new e.message
|
||||
end
|
||||
|
||||
when LibIPC::EventType::MessageTx
|
||||
Baguette::Log.debug "Message sent to #{event.fd}" if @configuration.print_ipc_message_sent
|
||||
|
||||
when LibIPC::EventType::Connection
|
||||
Baguette::Log.debug "Connection from #{event.fd}" if @configuration.print_ipc_connection
|
||||
when LibIPC::EventType::Disconnection
|
||||
Baguette::Log.debug "Disconnection from #{event.fd}" if @configuration.print_ipc_disconnection
|
||||
else
|
||||
Baguette::Log.error "Not implemented behavior for event: #{event}"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
begin
|
||||
simulation, no_configuration, configuration_file = Baguette::Configuration.option_parser
|
||||
|
||||
configuration = if no_configuration
|
||||
Baguette::Log.info "do not load a configuration file."
|
||||
Baguette::Configuration::Auth.new
|
||||
else
|
||||
Baguette::Configuration::Auth.get(configuration_file) ||
|
||||
Baguette::Configuration::Auth.new
|
||||
end
|
||||
|
||||
Baguette::Context.verbosity = configuration.verbosity
|
||||
|
||||
if key_file = configuration.shared_key_file
|
||||
configuration.shared_key = File.read(key_file).chomp
|
||||
end
|
||||
|
||||
OptionParser.parse do |parser|
|
||||
parser.banner = "usage: authd [options]"
|
||||
|
||||
parser.on "--storage directory", "Directory in which to store users." do |directory|
|
||||
configuration.storage = directory
|
||||
end
|
||||
|
||||
parser.on "-K file", "--key-file file", "JWT key file" do |file_name|
|
||||
configuration.shared_key = File.read(file_name).chomp
|
||||
end
|
||||
|
||||
parser.on "-R", "--allow-registrations", "Allow user registration." do
|
||||
configuration.registrations = true
|
||||
end
|
||||
|
||||
parser.on "-E", "--require-email", "Require an email." do
|
||||
configuration.require_email = true
|
||||
end
|
||||
|
||||
parser.on "-t activation-template-name", "--activation-template name", "Email activation template." do |opt|
|
||||
configuration.activation_template = opt
|
||||
end
|
||||
|
||||
parser.on "-r recovery-template-name", "--recovery-template name", "Email recovery template." do |opt|
|
||||
configuration.recovery_template = opt
|
||||
end
|
||||
|
||||
parser.on "-m mailer-exe", "--mailer mailer-exe", "Application to send registration emails." do |opt|
|
||||
configuration.mailer_exe = opt
|
||||
end
|
||||
|
||||
|
||||
parser.on "-x key", "--read-only-profile-key key", "Marks a user profile key as being read-only." do |key|
|
||||
configuration.read_only_profile_keys.push key
|
||||
end
|
||||
|
||||
parser.on "-h", "--help", "Show this help" do
|
||||
puts parser
|
||||
exit 0
|
||||
end
|
||||
end
|
||||
|
||||
if simulation
|
||||
pp! configuration
|
||||
exit 0
|
||||
end
|
||||
|
||||
AuthD::Service.new(configuration).run
|
||||
|
||||
rescue e : OptionParser::Exception
|
||||
Baguette::Log.error e.message
|
||||
rescue e
|
||||
Baguette::Log.error "exception raised: #{e.message}"
|
||||
e.backtrace.try &.each do |line|
|
||||
STDERR << " - " << line << '\n'
|
||||
end
|
||||
end
|
|
@ -0,0 +1,261 @@
|
|||
require "../../src/json"
|
||||
require "json"
|
||||
|
||||
module AuthD
|
||||
class Client < IPC
|
||||
property key : String
|
||||
property server_fd : Int32 = -1
|
||||
|
||||
def initialize
|
||||
super
|
||||
@key = ""
|
||||
fd = self.connect "auth"
|
||||
if fd.nil?
|
||||
raise "couldn't connect to 'auth' IPC service"
|
||||
end
|
||||
@server_fd = fd
|
||||
end
|
||||
|
||||
def read
|
||||
slice = self.read @server_fd
|
||||
m = IPCMessage::TypedMessage.deserialize slice
|
||||
m.not_nil!
|
||||
end
|
||||
|
||||
def get_token?(login : String, password : String) : String?
|
||||
send_now Request::GetToken.new login, password
|
||||
|
||||
response = AuthD.responses.parse_ipc_json read
|
||||
|
||||
if response.is_a?(Response::Token)
|
||||
response.token
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def get_user?(login : String, password : String) : AuthD::User::Public?
|
||||
send_now Request::GetUserByCredentials.new login, password
|
||||
|
||||
response = AuthD.responses.parse_ipc_json read
|
||||
|
||||
if response.is_a? Response::User
|
||||
response.user
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def get_user?(uid_or_login : Int32 | String) : ::AuthD::User::Public?
|
||||
send_now Request::GetUser.new uid_or_login
|
||||
|
||||
response = AuthD.responses.parse_ipc_json read
|
||||
|
||||
if response.is_a? Response::User
|
||||
response.user
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def send_now(msg : IPC::JSON)
|
||||
m = IPCMessage::TypedMessage.new msg.type.to_u8, msg.to_json
|
||||
write @server_fd, m
|
||||
end
|
||||
|
||||
def send_now(type : Request::Type, payload)
|
||||
m = IPCMessage::TypedMessage.new type.value.to_u8, payload
|
||||
write @server_fd, m
|
||||
end
|
||||
|
||||
def decode_token(token)
|
||||
user, meta = JWT.decode token, @key, JWT::Algorithm::HS256
|
||||
|
||||
user = ::AuthD::User::Public.from_json user.to_json
|
||||
|
||||
{user, meta}
|
||||
end
|
||||
|
||||
# FIXME: Extra options may be useful to implement here.
|
||||
def add_user(login : String, password : String,
|
||||
email : String?,
|
||||
phone : String?,
|
||||
profile : Hash(String, ::JSON::Any)?) : ::AuthD::User::Public | Exception
|
||||
|
||||
send_now Request::AddUser.new @key, login, password, email, phone, profile
|
||||
|
||||
response = AuthD.responses.parse_ipc_json read
|
||||
|
||||
case response
|
||||
when Response::UserAdded
|
||||
response.user
|
||||
when Response::Error
|
||||
raise Exception.new response.reason
|
||||
else
|
||||
# Should not happen in serialized connections, but…
|
||||
# it’ll happen if you run several requests at once.
|
||||
Exception.new
|
||||
end
|
||||
end
|
||||
|
||||
def validate_user(login : String, activation_key : String) : ::AuthD::User::Public | Exception
|
||||
send_now Request::ValidateUser.new login, activation_key
|
||||
|
||||
response = AuthD.responses.parse_ipc_json read
|
||||
|
||||
case response
|
||||
when Response::UserValidated
|
||||
response.user
|
||||
when Response::Error
|
||||
raise Exception.new response.reason
|
||||
else
|
||||
# Should not happen in serialized connections, but…
|
||||
# it’ll happen if you run several requests at once.
|
||||
Exception.new
|
||||
end
|
||||
end
|
||||
|
||||
def ask_password_recovery(uid_or_login : String | Int32, email : String)
|
||||
send_now Request::AskPasswordRecovery.new uid_or_login, email
|
||||
response = AuthD.responses.parse_ipc_json read
|
||||
|
||||
case response
|
||||
when Response::PasswordRecoverySent
|
||||
when Response::Error
|
||||
raise Exception.new response.reason
|
||||
else
|
||||
Exception.new
|
||||
end
|
||||
end
|
||||
|
||||
def change_password(uid_or_login : String | Int32, new_pass : String, renew_key : String)
|
||||
send_now Request::PasswordRecovery.new uid_or_login, renew_key, new_pass
|
||||
response = AuthD.responses.parse_ipc_json read
|
||||
|
||||
case response
|
||||
when Response::PasswordRecovered
|
||||
when Response::Error
|
||||
raise Exception.new response.reason
|
||||
else
|
||||
Exception.new
|
||||
end
|
||||
end
|
||||
|
||||
def register(login : String,
|
||||
password : String,
|
||||
email : String?,
|
||||
phone : String?,
|
||||
profile : Hash(String, ::JSON::Any)?) : ::AuthD::User::Public?
|
||||
|
||||
send_now Request::Register.new login, password, email, phone, profile
|
||||
response = AuthD.responses.parse_ipc_json read
|
||||
|
||||
case response
|
||||
when Response::UserAdded
|
||||
when Response::Error
|
||||
raise Exception.new response.reason
|
||||
end
|
||||
end
|
||||
|
||||
def mod_user(uid_or_login : Int32 | String, password : String? = nil, email : String? = nil, phone : String? = nil, avatar : String? = nil) : Bool | Exception
|
||||
request = Request::ModUser.new @key, uid_or_login
|
||||
|
||||
request.password = password if password
|
||||
request.email = email if email
|
||||
request.phone = phone if phone
|
||||
request.avatar = avatar if avatar
|
||||
|
||||
send_now request
|
||||
|
||||
response = AuthD.responses.parse_ipc_json read
|
||||
|
||||
case response
|
||||
when Response::UserEdited
|
||||
true
|
||||
when Response::Error
|
||||
Exception.new response.reason
|
||||
else
|
||||
Exception.new "???"
|
||||
end
|
||||
end
|
||||
|
||||
def check_permission(user : Int32, service_name : String, resource_name : String) : User::PermissionLevel
|
||||
request = Request::CheckPermission.new @key, user, service_name, resource_name
|
||||
|
||||
send_now request
|
||||
|
||||
response = AuthD.responses.parse_ipc_json read
|
||||
|
||||
case response
|
||||
when Response::PermissionCheck
|
||||
response.permission
|
||||
when Response
|
||||
raise Exception.new "unexpected response: #{response.type}"
|
||||
else
|
||||
raise Exception.new "unexpected response"
|
||||
end
|
||||
end
|
||||
|
||||
def set_permission(uid : Int32, service : String, resource : String, permission : User::PermissionLevel)
|
||||
request = Request::SetPermission.new @key, uid, service, resource, permission
|
||||
|
||||
send_now request
|
||||
|
||||
response = AuthD.responses.parse_ipc_json read
|
||||
|
||||
case response
|
||||
when Response::PermissionSet
|
||||
true
|
||||
when Response
|
||||
raise Exception.new "unexpected response: #{response.type}"
|
||||
else
|
||||
raise Exception.new "unexpected response"
|
||||
end
|
||||
end
|
||||
|
||||
def search_user(user_login : String)
|
||||
send_now Request::SearchUser.new user_login
|
||||
response = AuthD.responses.parse_ipc_json read
|
||||
|
||||
case response
|
||||
when Response::MatchingUsers
|
||||
response.users
|
||||
when Response::Error
|
||||
raise Exception.new response.reason
|
||||
else
|
||||
Exception.new
|
||||
end
|
||||
end
|
||||
|
||||
def edit_profile_content(user : Int32 | String, new_values)
|
||||
send_now Request::EditProfileContent.new key, user, new_values
|
||||
response = AuthD.responses.parse_ipc_json read
|
||||
|
||||
case response
|
||||
when Response::User
|
||||
response.user
|
||||
when Response::Error
|
||||
raise Exception.new response.reason
|
||||
else
|
||||
raise Exception.new "unexpected response"
|
||||
end
|
||||
end
|
||||
|
||||
def delete(user : Int32 | String, key : String)
|
||||
send_now Request::Delete.new user, key
|
||||
delete_
|
||||
end
|
||||
def delete(user : Int32 | String, login : String, pass : String)
|
||||
send_now Request::Delete.new user, login, pass
|
||||
delete_
|
||||
end
|
||||
def delete_
|
||||
response = AuthD.responses.parse_ipc_json read
|
||||
case response
|
||||
when Response::Error
|
||||
raise Exception.new response.reason
|
||||
end
|
||||
response
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,13 @@
|
|||
module AuthD
|
||||
class Exception < ::Exception
|
||||
end
|
||||
|
||||
class UserNotFound < ::Exception
|
||||
end
|
||||
|
||||
class AuthenticationInfoLacking < ::Exception
|
||||
end
|
||||
|
||||
class AdminAuthorizationException < ::Exception
|
||||
end
|
||||
end
|
|
@ -0,0 +1,29 @@
|
|||
require "json"
|
||||
|
||||
class AuthD::Token
|
||||
include JSON::Serializable
|
||||
|
||||
property login : String
|
||||
property uid : Int32
|
||||
|
||||
def initialize(@login, @uid)
|
||||
end
|
||||
|
||||
def to_h
|
||||
{
|
||||
:login => login,
|
||||
:uid => uid
|
||||
}
|
||||
end
|
||||
|
||||
def to_s(key)
|
||||
JWT.encode to_h, key, JWT::Algorithm::HS256
|
||||
end
|
||||
|
||||
def self.from_s(key, str)
|
||||
payload, meta = JWT.decode str, key, JWT::Algorithm::HS256
|
||||
|
||||
self.new payload["login"].as_s, payload["uid"].as_i
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
require "json"
|
||||
require "uuid"
|
||||
|
||||
class AuthD::User
|
||||
include JSON::Serializable
|
||||
|
||||
enum PermissionLevel
|
||||
None
|
||||
Read
|
||||
Edit
|
||||
Admin
|
||||
|
||||
def to_json(o)
|
||||
to_s.downcase.to_json o
|
||||
end
|
||||
end
|
||||
|
||||
class Contact
|
||||
include JSON::Serializable
|
||||
|
||||
# the activation key is removed once the user is validated
|
||||
property activation_key : String?
|
||||
property email : String?
|
||||
property phone : String?
|
||||
|
||||
def initialize(@email = nil, @phone = nil)
|
||||
@activation_key = UUID.random.to_s
|
||||
end
|
||||
end
|
||||
|
||||
# Public.
|
||||
property login : String
|
||||
property uid : Int32
|
||||
property profile : Hash(String, JSON::Any)?
|
||||
|
||||
# Private.
|
||||
property contact : Contact
|
||||
property password_hash : String
|
||||
property password_renew_key : String?
|
||||
# service => resource => permission level
|
||||
property permissions : Hash(String, Hash(String, PermissionLevel))
|
||||
property configuration : Hash(String, Hash(String, JSON::Any))
|
||||
property date_last_connection : Time? = nil
|
||||
property date_registration : Time? = nil
|
||||
|
||||
def to_token
|
||||
Token.new @login, @uid
|
||||
end
|
||||
|
||||
def initialize(@uid, @login, @password_hash)
|
||||
@contact = Contact.new
|
||||
@permissions = Hash(String, Hash(String, PermissionLevel)).new
|
||||
@configuration = Hash(String, Hash(String, JSON::Any)).new
|
||||
end
|
||||
|
||||
class Public
|
||||
include JSON::Serializable
|
||||
|
||||
property login : String
|
||||
property uid : Int32
|
||||
property profile : Hash(String, JSON::Any)?
|
||||
|
||||
property date_registration : Time?
|
||||
|
||||
def initialize(@uid, @login, @profile, @date_registration)
|
||||
end
|
||||
end
|
||||
|
||||
def to_public : Public
|
||||
Public.new @uid, @login, @profile, @date_registration
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
require "json"
|
||||
require "jwt"
|
||||
require "../src/main.cr"
|
||||
|
||||
require "baguette-crystal-base"
|
||||
|
||||
# Allows get configuration from a provided file.
|
||||
# See Baguette::Configuration::Base.get
|
||||
class Baguette::Configuration
|
||||
class Auth < IPC
|
||||
include YAML::Serializable
|
||||
|
||||
property login : String? = nil
|
||||
property pass : String? = nil
|
||||
property shared_key : String = "nico-nico-nii" # Default authd key, as per the specs. :eyes:
|
||||
property shared_key_file : String? = nil
|
||||
|
||||
def initialize
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Token and user classes.
|
||||
require "./authd/token.cr"
|
||||
require "./authd/user.cr"
|
||||
|
||||
# Requests and responses.
|
||||
require "./authd/exceptions"
|
||||
|
||||
# Requests and responses.
|
||||
require "./network"
|
||||
|
||||
# Functions to request the authd server.
|
||||
require "./authd/client.cr"
|
|
@ -0,0 +1,13 @@
|
|||
require "uuid"
|
||||
require "option_parser"
|
||||
require "openssl"
|
||||
require "colorize"
|
||||
require "jwt"
|
||||
require "grok"
|
||||
|
||||
require "dodb"
|
||||
require "baguette-crystal-base"
|
||||
|
||||
require "../src/main.cr"
|
||||
require "./libauth.cr"
|
||||
require "./authd.cr"
|
|
@ -0,0 +1,22 @@
|
|||
require "../src/main.cr"
|
||||
require "../src/json"
|
||||
|
||||
class IPC::JSON
|
||||
def handle(service : AuthD::Service)
|
||||
raise "unimplemented"
|
||||
end
|
||||
end
|
||||
|
||||
module AuthD
|
||||
class_getter requests = [] of IPC::JSON.class
|
||||
class_getter responses = [] of IPC::JSON.class
|
||||
end
|
||||
|
||||
class IPC
|
||||
def schedule(fd, m : (AuthD::Request | AuthD::Response))
|
||||
schedule fd, m.type.to_u8, m.to_json
|
||||
end
|
||||
end
|
||||
|
||||
require "./requests/*"
|
||||
require "./responses/*"
|
|
@ -0,0 +1,100 @@
|
|||
class AuthD::Request
|
||||
IPC::JSON.message AddUser, 1 do
|
||||
# Only clients that have the right shared key will be allowed
|
||||
# to create users.
|
||||
property shared_key : String
|
||||
|
||||
property login : String
|
||||
property password : String
|
||||
property email : String? = nil
|
||||
property phone : String? = nil
|
||||
property profile : Hash(String, JSON::Any)? = nil
|
||||
|
||||
def initialize(@shared_key, @login, @password, @email, @phone, @profile)
|
||||
end
|
||||
|
||||
def handle(authd : AuthD::Service)
|
||||
# No verification of the users' informations when an admin adds it.
|
||||
# No mail address verification.
|
||||
if @shared_key != authd.configuration.shared_key
|
||||
return Response::Error.new "invalid authentication key"
|
||||
end
|
||||
|
||||
if authd.users_per_login.get? @login
|
||||
return Response::Error.new "login already used"
|
||||
end
|
||||
|
||||
if authd.configuration.require_email && @email.nil?
|
||||
return Response::Error.new "email required"
|
||||
end
|
||||
|
||||
password_hash = authd.hash_password @password
|
||||
|
||||
uid = authd.new_uid
|
||||
|
||||
user = User.new uid, @login, password_hash
|
||||
user.contact.email = @email unless @email.nil?
|
||||
user.contact.phone = @phone unless @phone.nil?
|
||||
|
||||
@profile.try do |profile|
|
||||
user.profile = profile
|
||||
end
|
||||
|
||||
# We consider adding the user as a registration
|
||||
user.date_registration = Time.local
|
||||
|
||||
authd.users << user
|
||||
authd.new_uid_commit uid
|
||||
Response::UserAdded.new user.to_public
|
||||
end
|
||||
end
|
||||
AuthD.requests << AddUser
|
||||
|
||||
|
||||
IPC::JSON.message ModUser, 5 do
|
||||
property shared_key : String
|
||||
|
||||
property user : Int32 | String
|
||||
property password : String? = nil
|
||||
property email : String? = nil
|
||||
property phone : String? = nil
|
||||
property avatar : String? = nil
|
||||
|
||||
def initialize(@shared_key, @user)
|
||||
end
|
||||
|
||||
def handle(authd : AuthD::Service)
|
||||
if @shared_key != authd.configuration.shared_key
|
||||
return Response::Error.new "invalid authentication key"
|
||||
end
|
||||
|
||||
uid_or_login = @user
|
||||
user = if uid_or_login.is_a? Int32
|
||||
authd.users_per_uid.get? uid_or_login.to_s
|
||||
else
|
||||
authd.users_per_login.get? uid_or_login
|
||||
end
|
||||
|
||||
unless user
|
||||
return Response::Error.new "user not found"
|
||||
end
|
||||
|
||||
@password.try do |s|
|
||||
user.password_hash = authd.hash_password s
|
||||
end
|
||||
|
||||
@email.try do |email|
|
||||
user.contact.email = email
|
||||
end
|
||||
|
||||
@phone.try do |phone|
|
||||
user.contact.phone = phone
|
||||
end
|
||||
|
||||
authd.users_per_uid.update user.uid.to_s, user
|
||||
|
||||
Response::UserEdited.new user.uid
|
||||
end
|
||||
end
|
||||
AuthD.requests << ModUser
|
||||
end
|
|
@ -0,0 +1,46 @@
|
|||
class AuthD::Request
|
||||
IPC::JSON.message EditContacts, 16 do
|
||||
property token : String
|
||||
|
||||
property email : String? = nil
|
||||
property phone : String? = nil
|
||||
|
||||
def initialize(@token)
|
||||
end
|
||||
|
||||
def handle(authd : AuthD::Service)
|
||||
user = authd.get_user_from_token @token
|
||||
|
||||
return Response::Error.new "invalid user" unless user
|
||||
|
||||
if email = @email
|
||||
# FIXME: This *should* require checking the new mail, with
|
||||
# a new activation key and everything else.
|
||||
user.contact.email = email
|
||||
end
|
||||
|
||||
authd.users_per_uid.update user
|
||||
|
||||
Response::UserEdited.new user.uid
|
||||
end
|
||||
end
|
||||
AuthD.requests << EditContacts
|
||||
|
||||
IPC::JSON.message GetContacts, 18 do
|
||||
property token : String
|
||||
|
||||
def initialize(@token)
|
||||
end
|
||||
|
||||
def handle(authd : AuthD::Service)
|
||||
user = authd.get_user_from_token @token
|
||||
|
||||
return Response::Error.new "invalid user" unless user
|
||||
|
||||
_c = user.contact
|
||||
|
||||
Response::Contacts.new user.uid, _c.email, _c.phone
|
||||
end
|
||||
end
|
||||
AuthD.requests << GetContacts
|
||||
end
|
|
@ -0,0 +1,69 @@
|
|||
class AuthD::Request
|
||||
IPC::JSON.message Delete, 17 do
|
||||
# Deletion can be triggered by either an admin or the user.
|
||||
property shared_key : String? = nil
|
||||
|
||||
property login : String? = nil
|
||||
property password : String? = nil
|
||||
|
||||
property user : String | Int32
|
||||
|
||||
def initialize(@user, @login, @password)
|
||||
end
|
||||
def initialize(@user, @shared_key)
|
||||
end
|
||||
|
||||
def handle(authd : AuthD::Service)
|
||||
uid_or_login = @user
|
||||
user_to_delete = if uid_or_login.is_a? Int32
|
||||
authd.users_per_uid.get? uid_or_login.to_s
|
||||
else
|
||||
authd.users_per_login.get? uid_or_login
|
||||
end
|
||||
|
||||
if user_to_delete.nil?
|
||||
return Response::Error.new "invalid user"
|
||||
end
|
||||
|
||||
# Either the request comes from an admin or the user.
|
||||
# Shared key == admin, check the key.
|
||||
if key = @shared_key
|
||||
return Response::Error.new "unauthorized (wrong shared key)" unless key == authd.configuration.shared_key
|
||||
else
|
||||
login = @login
|
||||
pass = @password
|
||||
if login.nil? || pass.nil?
|
||||
return Response::Error.new "authentication failed (no shared key, no login)"
|
||||
end
|
||||
|
||||
# authenticate the user
|
||||
begin
|
||||
user = authd.users_per_login.get login
|
||||
rescue e : DODB::MissingEntry
|
||||
return Response::Error.new "invalid credentials"
|
||||
end
|
||||
|
||||
if user.nil?
|
||||
return Response::Error.new "invalid credentials"
|
||||
end
|
||||
|
||||
if user.password_hash != authd.hash_password pass
|
||||
return Response::Error.new "invalid credentials"
|
||||
end
|
||||
|
||||
# Is the user to delete the requesting user?
|
||||
if user.uid != user_to_delete.uid
|
||||
return Response::Error.new "invalid credentials"
|
||||
end
|
||||
end
|
||||
|
||||
# User or admin is now verified: let's proceed with the user deletion.
|
||||
authd.users_per_login.delete user_to_delete.login
|
||||
|
||||
# TODO: better response
|
||||
Response::User.new user_to_delete.to_public
|
||||
end
|
||||
end
|
||||
AuthD.requests << Delete
|
||||
|
||||
end
|
|
@ -0,0 +1,41 @@
|
|||
class AuthD::Request
|
||||
IPC::JSON.message ListUsers, 8 do
|
||||
property token : String? = nil
|
||||
property key : String? = nil
|
||||
|
||||
def initialize(@token, @key)
|
||||
end
|
||||
|
||||
def handle(authd : AuthD::Service)
|
||||
# FIXME: Lines too long, repeatedly (>80c with 4c tabs).
|
||||
@token.try do |token|
|
||||
user = authd.get_user_from_token token
|
||||
|
||||
return Response::Error.new "unauthorized (user not found from token)" unless user
|
||||
|
||||
# Test if the user is a moderator.
|
||||
if permissions = user.permissions["authd"]?
|
||||
if rights = permissions["*"]?
|
||||
if rights >= User::PermissionLevel::Read
|
||||
else
|
||||
raise AdminAuthorizationException.new "unauthorized (insufficient rights on '*')"
|
||||
end
|
||||
else
|
||||
raise AdminAuthorizationException.new "unauthorized (no rights on '*')"
|
||||
end
|
||||
else
|
||||
raise AdminAuthorizationException.new "unauthorized (user not in authd group)"
|
||||
end
|
||||
end
|
||||
|
||||
@key.try do |key|
|
||||
return Response::Error.new "unauthorized (wrong shared key)" unless key == authd.configuration.shared_key
|
||||
end
|
||||
|
||||
return Response::Error.new "unauthorized (no key nor token)" unless @key || @token
|
||||
|
||||
Response::UsersList.new authd.users.to_h.map &.[1].to_public
|
||||
end
|
||||
end
|
||||
AuthD.requests << ListUsers
|
||||
end
|
|
@ -0,0 +1,122 @@
|
|||
class AuthD::Request
|
||||
IPC::JSON.message UpdatePassword, 7 do
|
||||
property login : String
|
||||
property old_password : String
|
||||
property new_password : String
|
||||
|
||||
def initialize(@login, @old_password, @new_password)
|
||||
end
|
||||
|
||||
def handle(authd : AuthD::Service)
|
||||
user = authd.users_per_login.get? @login
|
||||
|
||||
unless user
|
||||
return Response::Error.new "invalid credentials"
|
||||
end
|
||||
|
||||
if authd.hash_password(@old_password) != user.password_hash
|
||||
return Response::Error.new "invalid credentials"
|
||||
end
|
||||
|
||||
user.password_hash = authd.hash_password @new_password
|
||||
|
||||
authd.users_per_uid.update user.uid.to_s, user
|
||||
|
||||
Response::UserEdited.new user.uid
|
||||
end
|
||||
end
|
||||
AuthD.requests << UpdatePassword
|
||||
|
||||
IPC::JSON.message PasswordRecovery, 11 do
|
||||
property user : Int32 | String
|
||||
property password_renew_key : String
|
||||
property new_password : String
|
||||
|
||||
def initialize(@user, @password_renew_key, @new_password)
|
||||
end
|
||||
|
||||
def handle(authd : AuthD::Service)
|
||||
uid_or_login = @user
|
||||
user = if uid_or_login.is_a? Int32
|
||||
authd.users_per_uid.get? uid_or_login.to_s
|
||||
else
|
||||
authd.users_per_login.get? uid_or_login
|
||||
end
|
||||
|
||||
if user.nil?
|
||||
return Response::Error.new "user not found"
|
||||
end
|
||||
|
||||
if user.password_renew_key == @password_renew_key
|
||||
user.password_hash = authd.hash_password @new_password
|
||||
else
|
||||
return Response::Error.new "renew key not valid"
|
||||
end
|
||||
|
||||
user.password_renew_key = nil
|
||||
|
||||
authd.users_per_uid.update user.uid.to_s, user
|
||||
|
||||
Response::PasswordRecovered.new user.to_public
|
||||
end
|
||||
end
|
||||
AuthD.requests << PasswordRecovery
|
||||
|
||||
IPC::JSON.message AskPasswordRecovery, 12 do
|
||||
property user : Int32 | String
|
||||
property email : String
|
||||
|
||||
def initialize(@user, @email)
|
||||
end
|
||||
|
||||
def handle(authd : AuthD::Service)
|
||||
uid_or_login = @user
|
||||
user = if uid_or_login.is_a? Int32
|
||||
authd.users_per_uid.get? uid_or_login.to_s
|
||||
else
|
||||
authd.users_per_login.get? uid_or_login
|
||||
end
|
||||
|
||||
if user.nil?
|
||||
return Response::Error.new "no such user"
|
||||
end
|
||||
|
||||
if user.contact.email != @email
|
||||
# Same error as when users are not found.
|
||||
return Response::Error.new "no such user"
|
||||
end
|
||||
|
||||
user.password_renew_key = UUID.random.to_s
|
||||
|
||||
authd.users_per_uid.update user.uid.to_s, user
|
||||
|
||||
# Once the user is created and stored, we try to contact him
|
||||
if authd.configuration.print_password_recovery_parameters
|
||||
pp! user.login,
|
||||
user.contact.email.not_nil!,
|
||||
user.password_renew_key.not_nil!
|
||||
end
|
||||
|
||||
mailer_exe = authd.configuration.mailer_exe
|
||||
template_name = authd.configuration.recovery_template
|
||||
|
||||
u_login = user.login
|
||||
u_email = user.contact.email.not_nil!
|
||||
u_token = user.password_renew_key.not_nil!
|
||||
|
||||
# Once the user is created and stored, we try to contact him.
|
||||
unless Process.run(mailer_exe,
|
||||
# PARAMETERS
|
||||
[ "send", template_name, u_email ],
|
||||
# ENV
|
||||
{ "LOGIN" => u_login, "TOKEN" => u_token },
|
||||
true # clear environment
|
||||
).success?
|
||||
raise "cannot contact user #{u_login} address #{u_email}"
|
||||
end
|
||||
|
||||
Response::PasswordRecoverySent.new user.to_public
|
||||
end
|
||||
end
|
||||
AuthD.requests << AskPasswordRecovery
|
||||
end
|
|
@ -0,0 +1,113 @@
|
|||
class AuthD::Request
|
||||
IPC::JSON.message CheckPermission, 9 do
|
||||
property shared_key : String? = nil
|
||||
property token : String? = nil
|
||||
|
||||
property user : Int32 | String
|
||||
property service : String
|
||||
property resource : String
|
||||
|
||||
def initialize(@shared_key, @user, @service, @resource)
|
||||
end
|
||||
|
||||
def handle(authd : AuthD::Service)
|
||||
authorized = false
|
||||
|
||||
if key = @shared_key
|
||||
if key == authd.configuration.shared_key
|
||||
authorized = true
|
||||
else
|
||||
return Response::Error.new "invalid key provided"
|
||||
end
|
||||
end
|
||||
|
||||
if token = @token
|
||||
user = authd.get_user_from_token token
|
||||
|
||||
if user.nil?
|
||||
return Response::Error.new "token does not match user"
|
||||
end
|
||||
|
||||
if user.login != @user && user.uid != @user
|
||||
return Response::Error.new "token does not match user"
|
||||
end
|
||||
|
||||
authorized = true
|
||||
end
|
||||
|
||||
unless authorized
|
||||
return Response::Error.new "unauthorized"
|
||||
end
|
||||
|
||||
user = case u = @user
|
||||
when .is_a? Int32
|
||||
authd.users_per_uid.get? u.to_s
|
||||
else
|
||||
authd.users_per_login.get? u
|
||||
end
|
||||
|
||||
if user.nil?
|
||||
return Response::Error.new "no such user"
|
||||
end
|
||||
|
||||
service = @service
|
||||
service_permissions = user.permissions[service]?
|
||||
|
||||
if service_permissions.nil?
|
||||
return Response::PermissionCheck.new service, @resource, user.uid, User::PermissionLevel::None
|
||||
end
|
||||
|
||||
resource_permissions = service_permissions[@resource]?
|
||||
|
||||
if resource_permissions.nil?
|
||||
return Response::PermissionCheck.new service, @resource, user.uid, User::PermissionLevel::None
|
||||
end
|
||||
|
||||
return Response::PermissionCheck.new service, @resource, user.uid, resource_permissions
|
||||
end
|
||||
end
|
||||
AuthD.requests << CheckPermission
|
||||
|
||||
IPC::JSON.message SetPermission, 10 do
|
||||
property shared_key : String
|
||||
|
||||
property user : Int32 | String
|
||||
property service : String
|
||||
property resource : String
|
||||
property permission : ::AuthD::User::PermissionLevel
|
||||
|
||||
def initialize(@shared_key, @user, @service, @resource, @permission)
|
||||
end
|
||||
|
||||
def handle(authd : AuthD::Service)
|
||||
unless @shared_key == authd.configuration.shared_key
|
||||
return Response::Error.new "unauthorized"
|
||||
end
|
||||
|
||||
user = authd.users_per_uid.get? @user.to_s
|
||||
|
||||
if user.nil?
|
||||
return Response::Error.new "no such user"
|
||||
end
|
||||
|
||||
service = @service
|
||||
service_permissions = user.permissions[service]?
|
||||
|
||||
if service_permissions.nil?
|
||||
service_permissions = Hash(String, User::PermissionLevel).new
|
||||
user.permissions[service] = service_permissions
|
||||
end
|
||||
|
||||
if @permission.none?
|
||||
service_permissions.delete @resource
|
||||
else
|
||||
service_permissions[@resource] = @permission
|
||||
end
|
||||
|
||||
authd.users_per_uid.update user.uid.to_s, user
|
||||
|
||||
Response::PermissionSet.new user.uid, service, @resource, @permission
|
||||
end
|
||||
end
|
||||
AuthD.requests << SetPermission
|
||||
end
|
|
@ -0,0 +1,93 @@
|
|||
class AuthD::Request
|
||||
IPC::JSON.message EditProfile, 14 do
|
||||
property token : String
|
||||
property new_profile : Hash(String, JSON::Any)
|
||||
|
||||
def initialize(@token, @new_profile)
|
||||
end
|
||||
|
||||
def handle(authd : AuthD::Service)
|
||||
user = authd.get_user_from_token @token
|
||||
|
||||
return Response::Error.new "invalid user" unless user
|
||||
|
||||
new_profile = @new_profile
|
||||
|
||||
profile = user.profile || Hash(String, JSON::Any).new
|
||||
|
||||
authd.configuration.read_only_profile_keys.each do |key|
|
||||
if new_profile[key]? != profile[key]?
|
||||
return Response::Error.new "tried to edit read only key"
|
||||
end
|
||||
end
|
||||
|
||||
user.profile = new_profile
|
||||
|
||||
authd.users_per_uid.update user.uid.to_s, user
|
||||
|
||||
Response::User.new user.to_public
|
||||
end
|
||||
end
|
||||
AuthD.requests << EditProfile
|
||||
|
||||
# Same as above, but doesn’t reset the whole profile, only resets elements
|
||||
# for which keys are present in `new_profile`.
|
||||
IPC::JSON.message EditProfileContent, 15 do
|
||||
property token : String? = nil
|
||||
|
||||
property shared_key : String? = nil
|
||||
property user : Int32 | String | Nil
|
||||
|
||||
property new_profile : Hash(String, JSON::Any)
|
||||
|
||||
def initialize(@shared_key, @user, @new_profile)
|
||||
end
|
||||
def initialize(@token, @new_profile)
|
||||
end
|
||||
|
||||
def handle(authd : AuthD::Service)
|
||||
user = if token = @token
|
||||
u = authd.get_user_from_token token
|
||||
raise UserNotFound.new unless u
|
||||
u
|
||||
elsif shared_key = @shared_key
|
||||
raise AdminAuthorizationException.new if shared_key != authd.configuration.shared_key
|
||||
|
||||
u = @user
|
||||
raise UserNotFound.new unless u
|
||||
|
||||
u = if u.is_a? Int32
|
||||
authd.users_per_uid.get? u.to_s
|
||||
else
|
||||
authd.users_per_login.get? u
|
||||
end
|
||||
raise UserNotFound.new unless u
|
||||
|
||||
u
|
||||
else
|
||||
raise AuthenticationInfoLacking.new
|
||||
end
|
||||
|
||||
new_profile = user.profile || Hash(String, JSON::Any).new
|
||||
|
||||
unless @shared_key
|
||||
authd.configuration.read_only_profile_keys.each do |key|
|
||||
if @new_profile.has_key? key
|
||||
return Response::Error.new "tried to edit read only key"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@new_profile.each do |key, value|
|
||||
new_profile[key] = value
|
||||
end
|
||||
|
||||
user.profile = new_profile
|
||||
|
||||
authd.users_per_uid.update user.uid.to_s, user
|
||||
|
||||
Response::User.new user.to_public
|
||||
end
|
||||
end
|
||||
AuthD.requests << EditProfileContent
|
||||
end
|
|
@ -0,0 +1,84 @@
|
|||
class AuthD::Request
|
||||
IPC::JSON.message Register, 6 do
|
||||
property login : String
|
||||
property password : String
|
||||
property email : String? = nil
|
||||
property phone : String? = nil
|
||||
property profile : Hash(String, JSON::Any)? = nil
|
||||
|
||||
def initialize(@login, @password, @email, @phone, @profile)
|
||||
end
|
||||
|
||||
def handle(authd : AuthD::Service)
|
||||
if ! authd.configuration.registrations
|
||||
return Response::Error.new "registrations not allowed"
|
||||
end
|
||||
|
||||
if authd.users_per_login.get? @login
|
||||
return Response::Error.new "login already used"
|
||||
end
|
||||
|
||||
if authd.configuration.require_email && @email.nil?
|
||||
return Response::Error.new "email required"
|
||||
end
|
||||
|
||||
if ! @email.nil?
|
||||
# Test on the email address format.
|
||||
grok = Grok.new [ "%{EMAILADDRESS:email}" ]
|
||||
result = grok.parse @email.not_nil!
|
||||
email = result["email"]?
|
||||
|
||||
if email.nil?
|
||||
return Response::Error.new "invalid email format"
|
||||
end
|
||||
end
|
||||
|
||||
# In this case we should not accept its registration.
|
||||
if @password.size < 4
|
||||
return Response::Error.new "password too short"
|
||||
end
|
||||
|
||||
uid = authd.new_uid
|
||||
password = authd.hash_password @password
|
||||
|
||||
user = User.new uid, @login, password
|
||||
user.contact.email = @email unless @email.nil?
|
||||
user.contact.phone = @phone unless @phone.nil?
|
||||
|
||||
@profile.try do |profile|
|
||||
user.profile = profile
|
||||
end
|
||||
|
||||
user.date_registration = Time.local
|
||||
|
||||
begin
|
||||
mailer_exe = authd.configuration.mailer_exe
|
||||
template_name = authd.configuration.activation_template
|
||||
|
||||
u_login = user.login
|
||||
u_email = user.contact.email.not_nil!
|
||||
u_activation_key = user.contact.activation_key.not_nil!
|
||||
|
||||
# Once the user is created and stored, we try to contact him.
|
||||
unless Process.run(mailer_exe,
|
||||
# PARAMETERS
|
||||
[ "send", template_name, u_email ],
|
||||
# ENV
|
||||
{ "LOGIN" => u_login, "TOKEN" => u_activation_key },
|
||||
true # clear environment
|
||||
).success?
|
||||
raise "cannot contact user #{u_login} address #{u_email}"
|
||||
end
|
||||
rescue e
|
||||
Baguette::Log.error "mailer: #{e}"
|
||||
return Response::Error.new "cannot contact the user (not registered)"
|
||||
end
|
||||
|
||||
# add the user only if we were able to send the confirmation mail
|
||||
authd.users << user
|
||||
authd.new_uid_commit uid
|
||||
Response::UserAdded.new user.to_public
|
||||
end
|
||||
end
|
||||
AuthD.requests << Register
|
||||
end
|
|
@ -0,0 +1,34 @@
|
|||
class AuthD::Request
|
||||
IPC::JSON.message SearchUser, 13 do
|
||||
property user : String
|
||||
|
||||
def initialize(@user)
|
||||
end
|
||||
|
||||
def handle(authd : AuthD::Service)
|
||||
pattern = Regex.new @user, Regex::Options::IGNORE_CASE
|
||||
|
||||
matching_users = Array(AuthD::User::Public).new
|
||||
|
||||
users = authd.users.to_a
|
||||
users.each do |u|
|
||||
if pattern =~ u.login || u.profile.try do |profile|
|
||||
full_name = profile["full_name"]?
|
||||
if full_name.nil?
|
||||
false
|
||||
else
|
||||
pattern =~ full_name.as_s
|
||||
end
|
||||
end
|
||||
Baguette::Log.debug "#{u.login} matches #{pattern}"
|
||||
matching_users << u.to_public
|
||||
else
|
||||
Baguette::Log.debug "#{u.login} doesn't match #{pattern}"
|
||||
end
|
||||
end
|
||||
|
||||
Response::MatchingUsers.new matching_users
|
||||
end
|
||||
end
|
||||
AuthD.requests << SearchUser
|
||||
end
|
|
@ -0,0 +1,34 @@
|
|||
class AuthD::Request
|
||||
IPC::JSON.message GetToken, 0 do
|
||||
property login : String
|
||||
property password : String
|
||||
|
||||
def initialize(@login, @password)
|
||||
end
|
||||
|
||||
def handle(authd : AuthD::Service)
|
||||
begin
|
||||
user = authd.users_per_login.get @login
|
||||
rescue e : DODB::MissingEntry
|
||||
return Response::Error.new "invalid credentials"
|
||||
end
|
||||
|
||||
if user.nil?
|
||||
return Response::Error.new "invalid credentials"
|
||||
end
|
||||
|
||||
if user.password_hash != authd.hash_password @password
|
||||
return Response::Error.new "invalid credentials"
|
||||
end
|
||||
|
||||
user.date_last_connection = Time.local
|
||||
token = user.to_token
|
||||
|
||||
# change the date of the last connection
|
||||
authd.users_per_uid.update user.uid.to_s, user
|
||||
|
||||
Response::Token.new (token.to_s authd.configuration.shared_key), user.uid
|
||||
end
|
||||
end
|
||||
AuthD.requests << GetToken
|
||||
end
|
|
@ -0,0 +1,84 @@
|
|||
class AuthD::Request
|
||||
IPC::JSON.message ValidateUser, 2 do
|
||||
property login : String
|
||||
property activation_key : String
|
||||
|
||||
def initialize(@login, @activation_key)
|
||||
end
|
||||
|
||||
def handle(authd : AuthD::Service)
|
||||
user = authd.users_per_login.get? @login
|
||||
|
||||
if user.nil?
|
||||
return Response::Error.new "user not found"
|
||||
end
|
||||
|
||||
if user.contact.activation_key.nil?
|
||||
return Response::Error.new "user already validated"
|
||||
end
|
||||
|
||||
# remove the user contact activation key: the email is validated
|
||||
if user.contact.activation_key == @activation_key
|
||||
user.contact.activation_key = nil
|
||||
else
|
||||
return Response::Error.new "wrong activation key"
|
||||
end
|
||||
|
||||
authd.users_per_uid.update user.uid.to_s, user
|
||||
|
||||
Response::UserValidated.new user.to_public
|
||||
end
|
||||
end
|
||||
AuthD.requests << ValidateUser
|
||||
|
||||
IPC::JSON.message GetUser, 3 do
|
||||
property user : Int32 | String
|
||||
|
||||
def initialize(@user)
|
||||
end
|
||||
|
||||
def handle(authd : AuthD::Service)
|
||||
uid_or_login = @user
|
||||
user = if uid_or_login.is_a? Int32
|
||||
authd.users_per_uid.get? uid_or_login.to_s
|
||||
else
|
||||
authd.users_per_login.get? uid_or_login
|
||||
end
|
||||
|
||||
if user.nil?
|
||||
return Response::Error.new "user not found"
|
||||
end
|
||||
|
||||
Response::User.new user.to_public
|
||||
end
|
||||
end
|
||||
AuthD.requests << GetUser
|
||||
|
||||
IPC::JSON.message GetUserByCredentials, 4 do
|
||||
property login : String
|
||||
property password : String
|
||||
|
||||
def initialize(@login, @password)
|
||||
end
|
||||
|
||||
def handle(authd : AuthD::Service)
|
||||
user = authd.users_per_login.get? @login
|
||||
|
||||
unless user
|
||||
return Response::Error.new "invalid credentials"
|
||||
end
|
||||
|
||||
if authd.hash_password(@password) != user.password_hash
|
||||
return Response::Error.new "invalid credentials"
|
||||
end
|
||||
|
||||
user.date_last_connection = Time.local
|
||||
|
||||
# change the date of the last connection
|
||||
authd.users_per_uid.update user.uid.to_s, user
|
||||
|
||||
Response::User.new user.to_public
|
||||
end
|
||||
end
|
||||
AuthD.requests << GetUserByCredentials
|
||||
end
|
|
@ -0,0 +1,10 @@
|
|||
class AuthD::Response
|
||||
IPC::JSON.message Contacts, 12 do
|
||||
property user : Int32
|
||||
property email : String? = nil
|
||||
property phone : String? = nil
|
||||
def initialize(@user, @email, @phone)
|
||||
end
|
||||
end
|
||||
AuthD.responses << Contacts
|
||||
end
|
|
@ -0,0 +1,8 @@
|
|||
class AuthD::Response
|
||||
IPC::JSON.message Error, 0 do
|
||||
property reason : String? = nil
|
||||
def initialize(@reason)
|
||||
end
|
||||
end
|
||||
AuthD.responses << Error
|
||||
end
|
|
@ -0,0 +1,15 @@
|
|||
class AuthD::Response
|
||||
IPC::JSON.message PasswordRecoverySent, 9 do
|
||||
property user : ::AuthD::User::Public
|
||||
def initialize(@user)
|
||||
end
|
||||
end
|
||||
AuthD.responses << PasswordRecoverySent
|
||||
|
||||
IPC::JSON.message PasswordRecovered, 10 do
|
||||
property user : ::AuthD::User::Public
|
||||
def initialize(@user)
|
||||
end
|
||||
end
|
||||
AuthD.responses << PasswordRecovered
|
||||
end
|
|
@ -0,0 +1,21 @@
|
|||
class AuthD::Response
|
||||
IPC::JSON.message PermissionCheck, 7 do
|
||||
property user : Int32
|
||||
property service : String
|
||||
property resource : String
|
||||
property permission : ::AuthD::User::PermissionLevel
|
||||
def initialize(@service, @resource, @user, @permission)
|
||||
end
|
||||
end
|
||||
AuthD.responses << PermissionCheck
|
||||
|
||||
IPC::JSON.message PermissionSet, 8 do
|
||||
property user : Int32
|
||||
property service : String
|
||||
property resource : String
|
||||
property permission : ::AuthD::User::PermissionLevel
|
||||
def initialize(@user, @service, @resource, @permission)
|
||||
end
|
||||
end
|
||||
AuthD.responses << PermissionSet
|
||||
end
|
|
@ -0,0 +1,9 @@
|
|||
class AuthD::Response
|
||||
IPC::JSON.message Token, 1 do
|
||||
property uid : Int32
|
||||
property token : String
|
||||
def initialize(@token, @uid)
|
||||
end
|
||||
end
|
||||
AuthD.responses << Token
|
||||
end
|
|
@ -0,0 +1,43 @@
|
|||
class AuthD::Response
|
||||
IPC::JSON.message User, 2 do
|
||||
property user : ::AuthD::User::Public
|
||||
def initialize(@user)
|
||||
end
|
||||
end
|
||||
AuthD.responses << User
|
||||
|
||||
IPC::JSON.message UserAdded, 3 do
|
||||
property user : ::AuthD::User::Public
|
||||
def initialize(@user)
|
||||
end
|
||||
end
|
||||
AuthD.responses << UserAdded
|
||||
|
||||
IPC::JSON.message UserEdited, 4 do
|
||||
property uid : Int32
|
||||
def initialize(@uid)
|
||||
end
|
||||
end
|
||||
AuthD.responses << UserEdited
|
||||
|
||||
IPC::JSON.message UserValidated, 5 do
|
||||
property user : ::AuthD::User::Public
|
||||
def initialize(@user)
|
||||
end
|
||||
end
|
||||
AuthD.responses << UserValidated
|
||||
|
||||
IPC::JSON.message UsersList, 6 do
|
||||
property users : Array(::AuthD::User::Public)
|
||||
def initialize(@users)
|
||||
end
|
||||
end
|
||||
AuthD.responses << UsersList
|
||||
|
||||
IPC::JSON.message MatchingUsers, 11 do
|
||||
property users : Array(::AuthD::User::Public)
|
||||
def initialize(@users)
|
||||
end
|
||||
end
|
||||
AuthD.responses << MatchingUsers
|
||||
end
|
|
@ -0,0 +1,46 @@
|
|||
LDPATH ?= /tmp/libipc/zig-impl/build
|
||||
SRC ?= ./bin/some-crystal-app
|
||||
|
||||
VG_OPTS = --leak-check=full -v
|
||||
VG_OPTS += --show-leak-kinds=all
|
||||
VG_OPTS += --suppressions=valgrind.suppressions
|
||||
VG_OPTS += --gen-suppressions=all
|
||||
|
||||
build:
|
||||
CRYSTAL_LIBRARY_PATH=$(LDPATH) shards build
|
||||
|
||||
valgrind:
|
||||
LD_LIBRARY_PATH=$(LDPATH) valgrind $(VG_OPTS) $(SRC)
|
||||
|
||||
run:
|
||||
LD_LIBRARY_PATH=$(LDPATH) $(SRC)
|
||||
|
||||
build-pongd:
|
||||
CRYSTAL_LIBRARY_PATH=$(LDPATH) shards build pongd
|
||||
|
||||
run-pongd:
|
||||
LD_LIBRARY_PATH=$(LDPATH) ./bin/pongd
|
||||
|
||||
build-authd:
|
||||
CRYSTAL_LIBRARY_PATH=$(LDPATH) shards build authd
|
||||
|
||||
run-authd:
|
||||
@-rm /tmp/libipc-run/auth 2>/dev/null || true
|
||||
# LD_LIBRARY_PATH=$(LDPATH) ./bin/authd -R -E
|
||||
LD_LIBRARY_PATH=$(LDPATH) ./bin/authd --allow-registrations --require-email $(PARAMS)
|
||||
|
||||
build-authc:
|
||||
CRYSTAL_LIBRARY_PATH=$(LDPATH) shards build authc
|
||||
|
||||
run-authc:
|
||||
LD_LIBRARY_PATH=$(LDPATH) ./bin/authc
|
||||
|
||||
run-test:
|
||||
crystal run src/libauth.cr
|
||||
|
||||
doc:
|
||||
crystal docs
|
||||
|
||||
ACCESS_LOGS ?= ./access.log
|
||||
serve-doc:
|
||||
darkhttpd docs/ --addr 127.0.0.1 --port 35001 --log $(ACCESS_LOGS)
|
|
@ -0,0 +1,38 @@
|
|||
name: some-crystal-app
|
||||
version: 0.1.0
|
||||
|
||||
authors:
|
||||
- Philippe Pittoli <karchnu@karchnu.fr>
|
||||
|
||||
targets:
|
||||
main:
|
||||
main: src/main.cr
|
||||
pongd:
|
||||
main: tests/pongd.cr
|
||||
authd:
|
||||
main: authd/main.cr
|
||||
authc:
|
||||
main: authc/authc.cr
|
||||
|
||||
crystal: 1.7.1
|
||||
|
||||
dependencies:
|
||||
grok:
|
||||
github: spinscale/grok.cr
|
||||
passwd:
|
||||
git: https://git.baguette.netlib.re/Baguette/passwd.cr
|
||||
branch: master
|
||||
jwt:
|
||||
github: crystal-community/jwt
|
||||
branch: master
|
||||
baguette-crystal-base:
|
||||
git: https://git.baguette.netlib.re/Baguette/baguette-crystal-base
|
||||
branch: master
|
||||
dodb:
|
||||
git: https://git.baguette.netlib.re/Baguette/dodb.cr
|
||||
branch: master
|
||||
cbor:
|
||||
git: https://git.baguette.netlib.re/Baguette/crystal-cbor
|
||||
branch: master
|
||||
|
||||
license: MIT
|
|
@ -0,0 +1,38 @@
|
|||
@[Link("ipc")]
|
||||
lib LibIPC
|
||||
enum EventType
|
||||
Error # Self explanatory.
|
||||
Connection # New user.
|
||||
Disconnection # User disconnected.
|
||||
MessageRx # Message received.
|
||||
MessageTx # Message sent.
|
||||
Timer # Timeout in the poll(2) function.
|
||||
External # Message received from a non IPC socket.
|
||||
SwitchRx # Switch subsystem: message received.
|
||||
SwitchTx # Switch subsystem: message send.
|
||||
end
|
||||
|
||||
fun init = ipc_context_init (Void**) : LibC::Int
|
||||
fun deinit = ipc_context_deinit (Void**) : Void
|
||||
|
||||
fun service_init = ipc_service_init (Void*, LibC::Int*, LibC::Char*, LibC::UInt16T) : LibC::Int
|
||||
fun connect_service = ipc_connect_service(Void*, LibC::Int*, LibC::Char*, LibC::UInt16T) : LibC::Int
|
||||
|
||||
# Context EventType index fd buffer buflen
|
||||
fun wait = ipc_wait_event(Void*, UInt8*, LibC::UInt64T*, LibC::Int*, UInt8*, LibC::UInt64T*) : LibC::Int
|
||||
|
||||
# Sending a message NOW.
|
||||
# WARNING: doesn't wait the fd to become available.
|
||||
fun write = ipc_write(Void*, LibC::Int, UInt8*, LibC::UInt64T) : LibC::Int
|
||||
# Sending a message (will wait the fd to become available for IO operations).
|
||||
fun schedule = ipc_schedule(Void*, LibC::Int, UInt8*, LibC::UInt64T) : LibC::Int
|
||||
|
||||
fun read = ipc_read_fd (Void*, LibC::Int, UInt8*, LibC::UInt64T*);
|
||||
|
||||
fun timer = ipc_context_timer (Void*, LibC::Int)
|
||||
|
||||
# Closing connections.
|
||||
fun close = ipc_close(Void*, LibC::UInt64T) : LibC::Int
|
||||
fun close_fd = ipc_close_fd(Void*, LibC::Int) : LibC::Int
|
||||
fun close_all = ipc_close_all(Void*) : LibC::Int
|
||||
end
|
|
@ -0,0 +1,59 @@
|
|||
require "cbor"
|
||||
require "./main.cr"
|
||||
|
||||
# IPC::CBOR is the root class for all exchanged messages (using CBOR).
|
||||
# IPC::CBOR inherited classes have a common 'type' class attribute,
|
||||
# which enables to find the right IPC::CBOR+ class given a TypedMessage's type.
|
||||
|
||||
# All transfered messages are typed messages.
|
||||
# TypedMessage = u8 type (= IPC::CBOR+ class type) + CBOR content.
|
||||
# 'CBOR content' being a serialized IPC::CBOR+ class.
|
||||
|
||||
# Conventionally, IPC::CBOR+ classes have a 'handle' method to process incoming messages.
|
||||
class IPC::CBOR
|
||||
include ::CBOR::Serializable
|
||||
|
||||
class_property type = -1
|
||||
property id : ::CBOR::Any?
|
||||
|
||||
def type
|
||||
@@type
|
||||
end
|
||||
|
||||
macro message(id, type, &block)
|
||||
class {{id}} < ::IPC::CBOR
|
||||
include ::CBOR::Serializable
|
||||
|
||||
@@type = {{type}}
|
||||
|
||||
{{yield}}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class IPC
|
||||
# Schedule messages contained into IPC::CBOR+.
|
||||
def schedule(fd : Int32, message : IPC::CBOR)
|
||||
typed_msg = IPCMessage::TypedMessage.new message.type.to_u8, message.to_cbor
|
||||
schedule fd, typed_msg
|
||||
end
|
||||
def write(fd : Int32, message : IPC::CBOR)
|
||||
typed_msg = IPCMessage::TypedMessage.new message.type.to_u8, message.to_cbor
|
||||
write fd, typed_msg
|
||||
end
|
||||
end
|
||||
|
||||
# CAUTION: only use this method on an Array(IPC::CBOR.class).
|
||||
class Array(T)
|
||||
def parse_ipc_cbor(message : IPCMessage::TypedMessage) : IPC::CBOR?
|
||||
message_type = find &.type.==(message.type)
|
||||
|
||||
payload = message.payload
|
||||
|
||||
if message_type.nil?
|
||||
raise "invalid message type (#{message.type})"
|
||||
end
|
||||
|
||||
message_type.from_cbor payload
|
||||
end
|
||||
end
|
|
@ -0,0 +1,134 @@
|
|||
class IPC
|
||||
# Reception buffer with a big capacity.
|
||||
# Allocated once.
|
||||
@reception_buffer = Array(UInt8).new 2_000_000
|
||||
@reception_buffer_len : LibC::UInt64T = 2_000_000
|
||||
|
||||
class Event
|
||||
property type : LibIPC::EventType
|
||||
property index : LibC::UInt64T
|
||||
property fd : Int32
|
||||
property message : Array(UInt8)? = nil
|
||||
|
||||
def initialize(t : UInt8, @index, @fd, buffer, buflen)
|
||||
@type = LibIPC::EventType.new t
|
||||
if buflen > 0
|
||||
# Array -> Pointer -> Slice -> Array
|
||||
@message = buffer.to_unsafe.to_slice(buflen).to_a
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def initialize()
|
||||
@context = Pointer(Void).null
|
||||
LibIPC.init(pointerof(@context))
|
||||
at_exit { deinit }
|
||||
end
|
||||
|
||||
# Closes all connections then remove the structure from memory.
|
||||
def deinit
|
||||
LibIPC.deinit(pointerof(@context))
|
||||
end
|
||||
|
||||
def service_init(name : String) : Int
|
||||
fd = uninitialized Int32
|
||||
if LibIPC.service_init(@context, pointerof(fd), name, name.size) != 0
|
||||
raise "oh noes, 'service_init' iz brkn"
|
||||
end
|
||||
fd
|
||||
end
|
||||
|
||||
def connect(name : String) : Int32
|
||||
fd = uninitialized Int32
|
||||
if LibIPC.connect_service(@context, pointerof(fd), name, name.size) != 0
|
||||
raise "oh noes, 'connect_service' iz brkn"
|
||||
end
|
||||
fd
|
||||
end
|
||||
|
||||
def timer(value : LibC::Int)
|
||||
LibIPC.timer(@context, value)
|
||||
end
|
||||
|
||||
def write(fd : Int, string : String)
|
||||
self.write(fd, string.to_unsafe, string.size.to_u64)
|
||||
end
|
||||
|
||||
def write(fd : Int, buffer : UInt8*, buflen : UInt64)
|
||||
if LibIPC.write(@context, fd, buffer, buflen) != 0
|
||||
raise "oh noes, 'write' iz brkn"
|
||||
end
|
||||
end
|
||||
|
||||
def write(fd : Int32, buffer : Bytes)
|
||||
self.write(fd, buffer.to_unsafe, buffer.size.to_u64)
|
||||
end
|
||||
|
||||
def read(fd : Int32) : Slice(UInt8)
|
||||
buffer : Bytes = Bytes.new 2000000
|
||||
size = buffer.size.to_u64
|
||||
LibIPC.read(@context, fd, buffer.to_unsafe, pointerof(size))
|
||||
buffer[0..size - 1]
|
||||
end
|
||||
|
||||
def schedule(fd : Int32, string : String)
|
||||
self.schedule(fd, string.to_unsafe, string.size.to_u64)
|
||||
end
|
||||
|
||||
def schedule(fd : Int32, buffer : Array(UInt8), buflen : Int32)
|
||||
self.schedule(fd, buffer.to_unsafe, buflen.to_u64)
|
||||
end
|
||||
|
||||
def schedule(fd : Int32, buffer : Bytes)
|
||||
self.schedule(fd, buffer.to_unsafe, buffer.size.to_u64)
|
||||
end
|
||||
|
||||
def schedule(fd : Int32, buffer : UInt8*, buflen : UInt64)
|
||||
if LibIPC.schedule(@context, fd, buffer, buflen) != 0
|
||||
raise "oh noes, 'schedule' iz brkn"
|
||||
end
|
||||
end
|
||||
|
||||
def close(index : LibC::UInt64T)
|
||||
if LibIPC.close(@context, index) != 0
|
||||
raise "Oh noes, 'close index' iz brkn"
|
||||
end
|
||||
end
|
||||
|
||||
def close(fd : LibC::Int)
|
||||
if LibIPC.close_fd(@context, fd) != 0
|
||||
raise "Oh noes, 'close fd' iz brkn"
|
||||
end
|
||||
end
|
||||
|
||||
def close
|
||||
if LibIPC.close_all(@context) != 0
|
||||
raise "Oh noes, 'close all' iz brkn"
|
||||
end
|
||||
end
|
||||
|
||||
def wait() : IPC::Event
|
||||
eventtype : UInt8 = 0
|
||||
index : LibC::UInt64T = 0
|
||||
fd : Int32 = 0
|
||||
buflen = @reception_buffer_len
|
||||
ret = LibIPC.wait(@context,
|
||||
pointerof(eventtype),
|
||||
pointerof(index),
|
||||
pointerof(fd),
|
||||
@reception_buffer.to_unsafe,
|
||||
pointerof(buflen))
|
||||
|
||||
if ret != 0
|
||||
raise "Oh noes, 'wait' iz brkn"
|
||||
end
|
||||
|
||||
Event.new(eventtype, index, fd, @reception_buffer, buflen)
|
||||
end
|
||||
|
||||
def loop(&block : Proc(IPC::Event, Nil))
|
||||
::loop do
|
||||
yield wait
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,58 @@
|
|||
require "json"
|
||||
|
||||
# IPC::JSON is the root class for all exchanged messages (using JSON).
|
||||
# IPC::JSON inherited classes have a common 'type' class attribute,
|
||||
# which enables to find the right IPC::JSON+ class given a TypedMessage's type.
|
||||
|
||||
# All transfered messages are typed messages.
|
||||
# TypedMessage = u8 type (= IPC::JSON+ class type) + JSON content.
|
||||
# 'JSON content' being a serialized IPC::JSON+ class.
|
||||
|
||||
# Conventionally, IPC::JSON+ classes have a 'handle' method to process incoming messages.
|
||||
class IPC::JSON
|
||||
include ::JSON::Serializable
|
||||
|
||||
class_property type = -1
|
||||
property id : ::JSON::Any?
|
||||
|
||||
def type
|
||||
@@type
|
||||
end
|
||||
|
||||
macro message(id, type, &block)
|
||||
class {{id}} < ::IPC::JSON
|
||||
include ::JSON::Serializable
|
||||
|
||||
@@type = {{type}}
|
||||
|
||||
{{yield}}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class IPC
|
||||
# Schedule messages contained into IPC::JSON+.
|
||||
def schedule(fd : Int32, message : IPC::JSON)
|
||||
typed_msg = IPCMessage::TypedMessage.new message.type.to_u8, message.to_json
|
||||
schedule fd, typed_msg
|
||||
end
|
||||
def write(fd : Int32, message : IPC::JSON)
|
||||
typed_msg = IPCMessage::TypedMessage.new message.type.to_u8, message.to_json
|
||||
write fd, typed_msg
|
||||
end
|
||||
end
|
||||
|
||||
# CAUTION: only use this method on an Array(IPC::JSON.class).
|
||||
class Array(T)
|
||||
def parse_ipc_json(message : IPCMessage::TypedMessage) : IPC::JSON?
|
||||
message_type = find &.type.==(message.type)
|
||||
|
||||
payload = String.new message.payload
|
||||
|
||||
if message_type.nil?
|
||||
raise "invalid message type (#{message.type})"
|
||||
end
|
||||
|
||||
message_type.from_json payload
|
||||
end
|
||||
end
|
|
@ -0,0 +1,3 @@
|
|||
require "./bindings.cr"
|
||||
require "./message.cr"
|
||||
require "./high-level-bindings.cr"
|
|
@ -0,0 +1,69 @@
|
|||
# TODO: tests.
|
||||
|
||||
# Serialization (and deserialization) doesn't refer to IPC format.
|
||||
# IPC serialization format: 'length + value'
|
||||
# IPCMessage serialization: 'value'
|
||||
# 'Value' is:
|
||||
# - simply the message payload for UntypedMessage
|
||||
# - type (u8) + payload for TypedMessage
|
||||
module IPCMessage
|
||||
class UntypedMessage
|
||||
property payload : Bytes
|
||||
def initialize(string : String)
|
||||
@payload = Bytes.new string.to_unsafe, string.size
|
||||
end
|
||||
def initialize(@payload)
|
||||
end
|
||||
|
||||
def self.deserialize(payload : Bytes) : UntypedMessage
|
||||
IPCMessage::UntypedMessage.new payload
|
||||
end
|
||||
|
||||
def serialize
|
||||
@payload
|
||||
end
|
||||
end
|
||||
|
||||
# WARNING: you can only have up to 256 types.
|
||||
class TypedMessage < UntypedMessage
|
||||
property type : UInt8? = nil
|
||||
def initialize(@type, string : String)
|
||||
super string
|
||||
end
|
||||
def initialize(@type, payload)
|
||||
super payload
|
||||
end
|
||||
def initialize(payload)
|
||||
super payload
|
||||
end
|
||||
|
||||
def self.deserialize(bytes : Bytes) : TypedMessage?
|
||||
if bytes.size == 0
|
||||
nil
|
||||
else
|
||||
type = bytes[0]
|
||||
IPCMessage::TypedMessage.new type, bytes[1..]
|
||||
end
|
||||
end
|
||||
|
||||
def serialize
|
||||
bytes = Bytes.new (1 + @payload.size)
|
||||
type = @type
|
||||
bytes[0] = type.nil? ? 0.to_u8 : type
|
||||
bytes[1..].copy_from @payload
|
||||
bytes
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Send both typed and untyped messages.
|
||||
class IPC
|
||||
def schedule(fd : Int32, m : (IPCMessage::TypedMessage | IPCMessage::UntypedMessage))
|
||||
payload = m.serialize
|
||||
schedule fd, payload
|
||||
end
|
||||
def write(fd : Int32, m : (IPCMessage::TypedMessage | IPCMessage::UntypedMessage))
|
||||
payload = m.serialize
|
||||
write fd, payload
|
||||
end
|
||||
end
|
|
@ -0,0 +1,38 @@
|
|||
require "../src/main.cr"
|
||||
|
||||
# In 5 messages: quit
|
||||
count = 5
|
||||
|
||||
ipc = IPC.new
|
||||
fd = ipc.service_init("pong")
|
||||
|
||||
ipc.loop do |event|
|
||||
case event.type
|
||||
when LibIPC::EventType::MessageRx
|
||||
m = event.message
|
||||
if m.nil?
|
||||
puts "No message"
|
||||
else
|
||||
received = String.new(m.to_unsafe, m.size)
|
||||
pp! received
|
||||
ipc.schedule event.fd, m, m.size
|
||||
end
|
||||
|
||||
when LibIPC::EventType::MessageTx
|
||||
puts "A message has been sent"
|
||||
count -= 1
|
||||
if count == 0
|
||||
exit
|
||||
end
|
||||
|
||||
when LibIPC::EventType::Connection
|
||||
puts "A client just connected #JOY"
|
||||
|
||||
when LibIPC::EventType::Disconnection
|
||||
puts "A client just disconnected #SAD"
|
||||
|
||||
else
|
||||
puts "Unexpected: #{event.type}"
|
||||
exit 1
|
||||
end
|
||||
end
|
|
@ -0,0 +1,46 @@
|
|||
require "../main.cr"
|
||||
|
||||
def test_high_level
|
||||
ipc = IPC.new
|
||||
fd = ipc.connect("pong")
|
||||
ipc.write(fd, "hello this is some value")
|
||||
event = ipc.wait()
|
||||
|
||||
m = event.message
|
||||
if m.nil?
|
||||
puts "No message"
|
||||
else
|
||||
pp! String.new(m.to_unsafe, m.size)
|
||||
end
|
||||
end
|
||||
|
||||
def test_loop
|
||||
ipc = IPC.new
|
||||
fd = ipc.connect("pong")
|
||||
ipc.schedule(fd, "hello this is some value")
|
||||
ipc.loop do |event|
|
||||
case event.type
|
||||
when LibIPC::EventType::MessageRx
|
||||
m = event.message
|
||||
if m.nil?
|
||||
puts "No message"
|
||||
else
|
||||
pp! String.new(m.to_unsafe, m.size)
|
||||
end
|
||||
exit 0
|
||||
when LibIPC::EventType::MessageTx
|
||||
puts "A message has been sent"
|
||||
else
|
||||
puts "Unexpected: #{event.type}"
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: Write documentation for `Some::Crystal::App`
|
||||
module Some::Crystal::App
|
||||
VERSION = "0.1.0"
|
||||
|
||||
test_high_level
|
||||
test_loop
|
||||
end
|
|
@ -0,0 +1,229 @@
|
|||
{
|
||||
<insert_a_suppression_name_here>
|
||||
Memcheck:Addr1
|
||||
obj:/usr/lib/libgc.so.1.5.0
|
||||
fun:GC_init_linux_data_start
|
||||
fun:GC_init
|
||||
fun:*GC::init:Nil
|
||||
fun:*Crystal::main<Int32, Pointer(Pointer(UInt8))>:Int32
|
||||
fun:main
|
||||
}
|
||||
{
|
||||
<insert_a_suppression_name_here>
|
||||
Memcheck:Cond
|
||||
fun:GC_push_all_eager
|
||||
fun:GC_with_callee_saves_pushed
|
||||
fun:GC_push_roots
|
||||
fun:GC_mark_some
|
||||
obj:/usr/lib/libgc.so.1.5.0
|
||||
fun:GC_try_to_collect_inner
|
||||
fun:GC_init
|
||||
fun:*GC::init:Nil
|
||||
fun:*Crystal::main<Int32, Pointer(Pointer(UInt8))>:Int32
|
||||
fun:main
|
||||
}
|
||||
{
|
||||
<insert_a_suppression_name_here>
|
||||
Memcheck:Cond
|
||||
fun:GC_push_all_eager
|
||||
fun:GC_with_callee_saves_pushed
|
||||
fun:GC_push_roots
|
||||
fun:GC_mark_some
|
||||
obj:/usr/lib/libgc.so.1.5.0
|
||||
fun:GC_try_to_collect_inner
|
||||
fun:GC_init
|
||||
fun:*GC::init:Nil
|
||||
fun:*Crystal::main<Int32, Pointer(Pointer(UInt8))>:Int32
|
||||
fun:main
|
||||
}
|
||||
{
|
||||
<insert_a_suppression_name_here>
|
||||
Memcheck:Cond
|
||||
fun:GC_mark_from
|
||||
obj:/usr/lib/libgc.so.1.5.0
|
||||
fun:GC_mark_some
|
||||
obj:/usr/lib/libgc.so.1.5.0
|
||||
fun:GC_try_to_collect_inner
|
||||
fun:GC_init
|
||||
fun:*GC::init:Nil
|
||||
fun:*Crystal::main<Int32, Pointer(Pointer(UInt8))>:Int32
|
||||
fun:main
|
||||
}
|
||||
{
|
||||
<insert_a_suppression_name_here>
|
||||
Memcheck:Cond
|
||||
fun:GC_mark_from
|
||||
obj:/usr/lib/libgc.so.1.5.0
|
||||
fun:GC_mark_some
|
||||
obj:/usr/lib/libgc.so.1.5.0
|
||||
fun:GC_try_to_collect_inner
|
||||
fun:GC_init
|
||||
fun:*GC::init:Nil
|
||||
fun:*Crystal::main<Int32, Pointer(Pointer(UInt8))>:Int32
|
||||
fun:main
|
||||
}
|
||||
{
|
||||
<insert_a_suppression_name_here>
|
||||
Memcheck:Cond
|
||||
fun:GC_mark_from
|
||||
obj:/usr/lib/libgc.so.1.5.0
|
||||
fun:GC_mark_some
|
||||
obj:/usr/lib/libgc.so.1.5.0
|
||||
fun:GC_try_to_collect_inner
|
||||
fun:GC_init
|
||||
fun:*GC::init:Nil
|
||||
fun:*Crystal::main<Int32, Pointer(Pointer(UInt8))>:Int32
|
||||
fun:main
|
||||
}
|
||||
{
|
||||
<insert_a_suppression_name_here>
|
||||
Memcheck:Cond
|
||||
fun:GC_mark_from
|
||||
obj:/usr/lib/libgc.so.1.5.0
|
||||
fun:GC_mark_some
|
||||
obj:/usr/lib/libgc.so.1.5.0
|
||||
fun:GC_try_to_collect_inner
|
||||
fun:GC_init
|
||||
fun:*GC::init:Nil
|
||||
fun:*Crystal::main<Int32, Pointer(Pointer(UInt8))>:Int32
|
||||
fun:main
|
||||
}
|
||||
{
|
||||
<insert_a_suppression_name_here>
|
||||
Memcheck:Cond
|
||||
fun:GC_mark_from
|
||||
obj:/usr/lib/libgc.so.1.5.0
|
||||
fun:GC_mark_some
|
||||
obj:/usr/lib/libgc.so.1.5.0
|
||||
fun:GC_try_to_collect_inner
|
||||
fun:GC_init
|
||||
fun:*GC::init:Nil
|
||||
fun:*Crystal::main<Int32, Pointer(Pointer(UInt8))>:Int32
|
||||
fun:main
|
||||
}
|
||||
{
|
||||
<insert_a_suppression_name_here>
|
||||
Memcheck:Leak
|
||||
match-leak-kinds: definite
|
||||
fun:memalign
|
||||
fun:posix_memalign
|
||||
obj:/tmp/libipc/zig-impl/build/libipc.so.0.1.0
|
||||
fun:*Crystal::main_user_code<Int32, Pointer(Pointer(UInt8))>:Nil
|
||||
fun:*Crystal::main<Int32, Pointer(Pointer(UInt8))>:Int32
|
||||
fun:main
|
||||
}
|
||||
{
|
||||
<insert_a_suppression_name_here>
|
||||
Memcheck:Leak
|
||||
match-leak-kinds: definite
|
||||
fun:malloc
|
||||
fun:*Signal::setup_segfault_handler:(Int32 | Nil)
|
||||
fun:*Exception::CallStack::setup_crash_handler:(Int32 | Nil)
|
||||
fun:__crystal_main
|
||||
fun:*Crystal::main_user_code<Int32, Pointer(Pointer(UInt8))>:Nil
|
||||
fun:*Crystal::main<Int32, Pointer(Pointer(UInt8))>:Int32
|
||||
fun:main
|
||||
}
|
||||
{
|
||||
<insert_a_suppression_name_here>
|
||||
Memcheck:Cond
|
||||
fun:GC_mark_from
|
||||
obj:/usr/lib/libgc.so.1.5.0
|
||||
fun:GC_mark_some
|
||||
obj:/usr/lib/libgc.so.1.5.0
|
||||
fun:GC_try_to_collect_inner
|
||||
fun:GC_init
|
||||
fun:*GC::init:Nil
|
||||
fun:*Crystal::main<Int32, Pointer(Pointer(UInt8))>:Int32
|
||||
fun:main
|
||||
}
|
||||
{
|
||||
<insert_a_suppression_name_here>
|
||||
Memcheck:Cond
|
||||
fun:GC_push_all_eager
|
||||
fun:GC_with_callee_saves_pushed
|
||||
fun:GC_push_roots
|
||||
fun:GC_mark_some
|
||||
obj:/usr/lib/libgc.so.1.5.0
|
||||
fun:GC_try_to_collect_inner
|
||||
fun:GC_init
|
||||
fun:*GC::init:Nil
|
||||
fun:*Crystal::main<Int32, Pointer(Pointer(UInt8))>:Int32
|
||||
fun:main
|
||||
}
|
||||
{
|
||||
<insert_a_suppression_name_here>
|
||||
Memcheck:Addr1
|
||||
obj:/usr/lib/libgc.so.1.5.0
|
||||
fun:GC_init_linux_data_start
|
||||
fun:GC_init
|
||||
fun:*GC::init:Nil
|
||||
fun:*Crystal::main<Int32, Pointer(Pointer(UInt8))>:Int32
|
||||
fun:main
|
||||
}
|
||||
{
|
||||
<insert_a_suppression_name_here>
|
||||
Memcheck:Cond
|
||||
fun:GC_mark_from
|
||||
obj:/usr/lib/libgc.so.1.5.0
|
||||
fun:GC_mark_some
|
||||
obj:/usr/lib/libgc.so.1.5.0
|
||||
fun:GC_try_to_collect_inner
|
||||
fun:GC_init
|
||||
fun:*GC::init:Nil
|
||||
fun:*Crystal::main<Int32, Pointer(Pointer(UInt8))>:Int32
|
||||
fun:main
|
||||
}
|
||||
{
|
||||
<insert_a_suppression_name_here>
|
||||
Memcheck:Cond
|
||||
fun:GC_mark_from
|
||||
obj:/usr/lib/libgc.so.1.5.0
|
||||
fun:GC_mark_some
|
||||
obj:/usr/lib/libgc.so.1.5.0
|
||||
fun:GC_try_to_collect_inner
|
||||
fun:GC_init
|
||||
fun:*GC::init:Nil
|
||||
fun:*Crystal::main<Int32, Pointer(Pointer(UInt8))>:Int32
|
||||
fun:main
|
||||
}
|
||||
{
|
||||
<insert_a_suppression_name_here>
|
||||
Memcheck:Cond
|
||||
fun:GC_push_all_eager
|
||||
fun:GC_with_callee_saves_pushed
|
||||
fun:GC_push_roots
|
||||
fun:GC_mark_some
|
||||
obj:/usr/lib/libgc.so.1.5.0
|
||||
fun:GC_try_to_collect_inner
|
||||
fun:GC_init
|
||||
fun:*GC::init:Nil
|
||||
fun:*Crystal::main<Int32, Pointer(Pointer(UInt8))>:Int32
|
||||
fun:main
|
||||
}
|
||||
{
|
||||
<insert_a_suppression_name_here>
|
||||
Memcheck:Cond
|
||||
fun:GC_mark_from
|
||||
obj:/usr/lib/libgc.so.1.5.0
|
||||
fun:GC_mark_some
|
||||
obj:/usr/lib/libgc.so.1.5.0
|
||||
fun:GC_try_to_collect_inner
|
||||
fun:GC_init
|
||||
fun:*GC::init:Nil
|
||||
fun:*Crystal::main<Int32, Pointer(Pointer(UInt8))>:Int32
|
||||
fun:main
|
||||
}
|
||||
{
|
||||
<insert_a_suppression_name_here>
|
||||
Memcheck:Cond
|
||||
fun:GC_mark_from
|
||||
obj:/usr/lib/libgc.so.1.5.0
|
||||
fun:GC_mark_some
|
||||
obj:/usr/lib/libgc.so.1.5.0
|
||||
fun:GC_try_to_collect_inner
|
||||
fun:GC_init
|
||||
fun:*GC::init:Nil
|
||||
fun:*Crystal::main<Int32, Pointer(Pointer(UInt8))>:Int32
|
||||
fun:main
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
ZIGC=zig
|
||||
|
||||
CC=gcc
|
||||
CFLAGS=-Wall -Wextra
|
||||
LDFLAGS=-I build/ -L build/ -lipc
|
||||
|
||||
all: zigcompilation compilation
|
||||
|
||||
ifeq ($(SRC),)
|
||||
test-src:
|
||||
@echo SRC must be set via command line.
|
||||
@exit 1
|
||||
else
|
||||
test-src:
|
||||
endif
|
||||
|
||||
list-obj-files: test-src
|
||||
@# List all .o included in a .a archive.
|
||||
ar t $(SRC)
|
||||
list-symbols: test-src
|
||||
@# List all symbols in a .so.
|
||||
nm -D $(SRC)
|
||||
list-symbols-alt: test-src
|
||||
@# Alternative: grep .text section in an objdump output.
|
||||
objdump -T $(SRC) | grep text
|
||||
|
||||
zigcompilation: build.zig src/*.zig
|
||||
$(ZIGC) build
|
||||
|
||||
compilation: src/main.c
|
||||
@echo the following compilation will produce errors despite actually working
|
||||
$(CC) -o main build/libipc.so $(CFLAGS) $^ $(LDFLAGS)
|
||||
|
||||
run:
|
||||
LD_LIBRARY_PATH=build ./main
|
||||
|
||||
valgrind:
|
||||
LD_LIBRARY_PATH=build valgrind --suppressions=./suppress-stuff.suppr --gen-suppressions=all -v --leak-check=full ./main
|
|
@ -0,0 +1,25 @@
|
|||
const std = @import("std");
|
||||
|
||||
pub fn build(b: *std.build.Builder) void {
|
||||
// Standard release options allow the person running `zig build` to select
|
||||
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
|
||||
const mode = b.standardReleaseOptions();
|
||||
|
||||
const lib = b.addStaticLibrary("ipc", "src/main.zig");
|
||||
lib.setOutputDir("build");
|
||||
lib.linkLibC();
|
||||
lib.setBuildMode(mode);
|
||||
lib.install();
|
||||
|
||||
const solib = b.addSharedLibrary("ipc", "src/main.zig", b.version(0, 0, 1));
|
||||
solib.setOutputDir("build");
|
||||
solib.linkLibC();
|
||||
solib.setBuildMode(mode);
|
||||
solib.install();
|
||||
|
||||
const main_tests = b.addTest("src/main.zig");
|
||||
main_tests.setBuildMode(mode);
|
||||
|
||||
const test_step = b.step("test", "Run library tests");
|
||||
test_step.dependOn(&main_tests.step);
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main(void) {
|
||||
int ret = 0;
|
||||
|
||||
printf ("Init context.\n");
|
||||
void *ctx = NULL;
|
||||
ret = ipc_context_init (&ctx);
|
||||
|
||||
if (ret != 0) {
|
||||
printf ("Cannot init context.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// TODO: do stuff
|
||||
|
||||
printf ("Deinit context\n");
|
||||
ipc_context_deinit (ctx);
|
||||
printf ("Context deinit.\n");
|
||||
free(ctx);
|
||||
printf ("Context completely freed.\n");
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
const std = @import("std");
|
||||
const fmt = std.fmt;
|
||||
|
||||
const print = std.debug.print;
|
||||
|
||||
pub const Context = struct {
|
||||
rundir: [] u8,
|
||||
allocator: std.mem.Allocator,
|
||||
const Self = @This();
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator) !Self {
|
||||
var rundir = try allocator.dupeZ(u8, "/tmp/libipc-run/");
|
||||
return Self { .rundir = rundir, .allocator = allocator };
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
self.allocator.free(self.rundir);
|
||||
}
|
||||
};
|
||||
|
||||
export fn ipc_context_init (ptr: **Context) callconv(.C) i32 {
|
||||
ptr.* = std.heap.c_allocator.create(Context) catch return 1;
|
||||
|
||||
ptr.*.* = Context.init(std.heap.c_allocator) catch |err| {
|
||||
print ("libipc: error while init context: {}\n", .{err});
|
||||
return 1;
|
||||
};
|
||||
return 0;
|
||||
}
|
||||
|
||||
export fn ipc_context_deinit (ctx: *Context) callconv(.C) void {
|
||||
ctx.deinit();
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
#ifndef LIBIPC
|
||||
#define LIBIPC
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
enum event_types {
|
||||
ERROR = 0 // A problem occured.
|
||||
, CONNECTION = 1 // New user.
|
||||
, DISCONNECTION = 2 // User disconnected.
|
||||
, MESSAGE_RX = 3 // New message.
|
||||
, MESSAGE_TX = 4 // Message sent.
|
||||
, TIMER = 5 // Timeout in the poll(2) function.
|
||||
, EXTERNAL = 6 // Message received from a non IPC socket.
|
||||
, SWITCH_RX = 7 // Message received from a switched FD.
|
||||
, SWITCH_TX = 8 // Message sent to a switched fd.
|
||||
};
|
||||
|
||||
// Return type of callback functions when switching.
|
||||
enum cb_event_types {
|
||||
CB_NO_ERROR = 0 // No error. A message was generated.
|
||||
, CB_ERROR = 1 // Generic error.
|
||||
, CB_FD_CLOSING = 2 // The fd is closing.
|
||||
, CB_IGNORE = 3 // The message should be ignored (protocol specific).
|
||||
};
|
||||
|
||||
int ipc_context_init (void** ptr);
|
||||
int ipc_service_init (void* ctx, int* servicefd, const char* service_name, uint16_t service_name_len);
|
||||
int ipc_connect_service (void* ctx, int* servicefd, const char* service_name, uint16_t service_name_len);
|
||||
void ipc_context_deinit (void** ctx);
|
||||
int ipc_write (void* ctx, int servicefd, char* mcontent, uint32_t mlen);
|
||||
int ipc_schedule (void* ctx, int servicefd, const char* mcontent, uint32_t mlen);
|
||||
int ipc_read_fd (void* ctx, int fd, char* buffer, size_t* buflen);
|
||||
int ipc_read (void* ctx, size_t index, char* buffer, size_t* buflen);
|
||||
int ipc_wait_event(void* ctx, char* t, size_t* index, int* originfd, char* buffer, size_t* buflen);
|
||||
void ipc_context_timer (void* ctx, int timer);
|
||||
int ipc_close_fd (void* ctx, int fd);
|
||||
int ipc_close (void* ctx, size_t index);
|
||||
int ipc_close_all (void* ctx);
|
||||
|
||||
// Switch functions (for "protocol" services, such as TCPd).
|
||||
int ipc_add_external (void* ctx, int newfd);
|
||||
int ipc_add_switch (void* ctx, int fd1, int fd2);
|
||||
|
||||
int ipc_set_switch_callbacks (void* ctx, int fd
|
||||
, enum cb_event_types (*in (int orig, const char *payload, uint32_t *mlen))
|
||||
, enum cb_event_types (*out(int dest, char *payload, uint32_t mlen)));
|
||||
|
||||
#endif
|
|
@ -0,0 +1,47 @@
|
|||
all:
|
||||
|
||||
ZIGMAKEDOC = -femit-docs -fno-emit-bin
|
||||
ZIGOPTIM ?= Debug
|
||||
# Linking against libc is almost mandatory, C allocator is used
|
||||
# for switching (default reception and emission functions).
|
||||
ZIGBUSEOPTS ?= -O$(ZIGOPTIM) -freference-trace -lc
|
||||
ZIGUSROPTS ?=
|
||||
ZIGC ?= zig
|
||||
ZIGOPTS ?= $(ZIGBUSEOPTS) $(ZIGUSROPTS)
|
||||
|
||||
# Debug with valgrind.
|
||||
ifdef VG_SUPPRESS_WARNINGS
|
||||
VALGRIND_SUPPRESS_WARNINGS ?= --suppressions=./valgrind.suppr
|
||||
endif
|
||||
ifdef VG_GENERATE_SUPPRESSION
|
||||
VALGRIND_GEN_SUPPRESSION ?= --gen-suppressions=all
|
||||
endif
|
||||
VALGRIND_OPTS=-v --leak-check=full --track-origins=yes
|
||||
ifdef USE_VALGRIND
|
||||
VALGRIND ?= valgrind $(VALGRIND_SUPPRESS_WARNINGS) \
|
||||
$(VALGRIND_GEN_SUPPRESSION) \
|
||||
$(VALGRIND_OPTS)
|
||||
endif
|
||||
# Optional parameters (copied here to help with autocompletion).
|
||||
VG_SUPPRESS_WARNINGS ?=
|
||||
VG_GENERATE_SUPPRESSION ?=
|
||||
USE_VALGRIND ?=
|
||||
|
||||
TO_CLEAN != ls misc/*.zig | sed 's/.zig$\//' | sed 's_misc/__'
|
||||
TO_CLEAN += bin/ipcd bin/tcpd bin/pong bin/pongd
|
||||
TO_CLEAN += bin/*.o
|
||||
clean:
|
||||
@-rm $(TO_CLEAN) 2>/dev/null
|
||||
|
||||
mrproper: clean
|
||||
@-rm -r docs build zig-cache zig-out 2>/dev/null
|
||||
|
||||
doc: src/ipcd.zig
|
||||
$(ZIGC) build-exe $(ZIGOPTS) $(ZIGMAKEDOC) $^
|
||||
|
||||
ACCESS_LOGS ?= ./access.log
|
||||
serve-doc:
|
||||
darkhttpd docs/ --addr 127.0.0.1 --port 35000 --log $(ACCESS_LOGS)
|
||||
|
||||
# You can add your specific instructions there.
|
||||
-include makefile.user
|
|
@ -0,0 +1,92 @@
|
|||
CC=gcc
|
||||
CFLAGS=-Wall -Wextra #-Wno-implicit-function-declaration
|
||||
LDFLAGS=-I build/ -L build/ -lipc
|
||||
|
||||
bin: build.zig apps/*.zig src/*.zig
|
||||
$(ZIGC) build
|
||||
|
||||
lib: build.zig src/*.zig
|
||||
$(ZIGC) build
|
||||
|
||||
stop-ipcd:
|
||||
-pkill -1 ipcd
|
||||
|
||||
stop-tcpd:
|
||||
-pkill -1 tcpd
|
||||
|
||||
run-ipcd:
|
||||
-rm /tmp/libipc-run/ipc 2>/dev/null || true
|
||||
$(VALGRIND) ./bin/ipcd
|
||||
|
||||
run-pongd:
|
||||
-rm /tmp/libipc-run/pong 2>/dev/null || true
|
||||
$(VALGRIND) ./bin/pongd
|
||||
|
||||
run-tcpd:
|
||||
@-rm /tmp/libipc-run/tcp 2>/dev/null || true
|
||||
$(VALGRIND) ./bin/tcpd
|
||||
|
||||
TCP_SERVICE_ALT ?= 127.0.0.1:9898
|
||||
run-tcpd-alternative:
|
||||
-rm /tmp/libipc-run/tcpdup 2>/dev/null || true
|
||||
IPC_SERVICE_NAME=tcpdup ADDRESS=$(TCP_SERVICE_ALT) $(VALGRIND) ./tcpd
|
||||
|
||||
SERVICE_NAME ?= p
|
||||
IPC_NETWORK ?= p unix://pong
|
||||
run-pong:
|
||||
@#Force pong to contact IPCd.
|
||||
@#SERVICE is the service to contact and IPC_NETWORK is the IPCd
|
||||
@#configuration to translate "p" into "pong" (still using UNIX
|
||||
@#sockets on the same computer).
|
||||
SERVICE="$(SERVICE_NAME)" IPC_NETWORK="$(IPC_NETWORK)" $(VALGRIND) ./bin/pong
|
||||
|
||||
run-pong-test-tcpd:
|
||||
SERVICE="pong" IPC_NETWORK="pong tcp://$(TCP_SERVICE_ALT)/pong" $(VALGRIND) ./pong
|
||||
|
||||
ifeq ($(SRC),)
|
||||
test-src:
|
||||
@echo SRC must be set via command line.
|
||||
@exit 1
|
||||
else
|
||||
test-src:
|
||||
endif
|
||||
|
||||
comp: bin test-bindings-pong test-bindings-pongd
|
||||
|
||||
list-obj-files: test-src
|
||||
@# List all .o included in a .a archive.
|
||||
ar t $(SRC)
|
||||
list-symbols: test-src
|
||||
@# List all symbols in a .so.
|
||||
nm -D $(SRC)
|
||||
list-symbols-alt: test-src
|
||||
@# Alternative: grep .text section in an objdump output.
|
||||
objdump -T $(SRC) | grep text
|
||||
|
||||
bindings-compile-pong: test-bindings/pong.c
|
||||
@-mkdir bin-bindings 2>/dev/null || true
|
||||
$(CC) -o bin-bindings/pong build/libipc.so $(CFLAGS) $^ $(LDFLAGS)
|
||||
|
||||
bindings-compile-pongd: test-bindings/pongd.c
|
||||
@-mkdir bin-bindings 2>/dev/null || true
|
||||
$(CC) -o bin-bindings/pongd build/libipc.so $(CFLAGS) $^ $(LDFLAGS)
|
||||
|
||||
bindings-test-pong:
|
||||
LD_LIBRARY_PATH=build/ $(VALGRIND) ./bin-bindings/pong
|
||||
|
||||
bindings-test-pongd:
|
||||
-rm /tmp/libipc-run/pong 2>/dev/null || true
|
||||
LD_LIBRARY_PATH=build/ $(VALGRIND) ./bin-bindings/pongd
|
||||
|
||||
WS_SERVICE ?= 127.0.0.1:8080
|
||||
TCP_SERVICE ?= 127.0.0.1:9000
|
||||
init-websocket-tcpd:
|
||||
@# '-b' binary, '-E' quit on end-of-file, 'ws-l' websocket URI to listen
|
||||
@# each connection is redirected to last parameter
|
||||
websocat -b -E ws-l:$(WS_SERVICE) tcp:$(TCP_SERVICE)
|
||||
|
||||
init-websocket-client:
|
||||
@# websocat -b -E tcp-l:127.0.0.1:9000 ws://127.0.0.1:9999
|
||||
websocat -b -E ws://$(WS_SERVICE)
|
||||
|
||||
.PHONY: bin lib
|
|
@ -0,0 +1,63 @@
|
|||
const std = @import("std");
|
||||
|
||||
/// TODO: move this to std
|
||||
|
||||
/// This definition enables the use of Zig types with a cmsghdr structure.
|
||||
/// The oddity of this layout is that the data must be aligned to @sizeOf(usize)
|
||||
/// rather than its natural alignment.
|
||||
pub fn Cmsghdr(comptime T: type) type {
|
||||
const Header = extern struct {
|
||||
len: usize,
|
||||
level: c_int,
|
||||
@"type": c_int,
|
||||
};
|
||||
|
||||
const data_align = @sizeOf(usize);
|
||||
const data_offset = std.mem.alignForward(@sizeOf(Header), data_align);
|
||||
|
||||
return extern struct {
|
||||
const Self = @This();
|
||||
|
||||
bytes: [data_offset + @sizeOf(T)]u8 align(@alignOf(Header)),
|
||||
|
||||
pub fn init(args: struct {
|
||||
level: c_int,
|
||||
@"type": c_int,
|
||||
data: T,
|
||||
}) Self {
|
||||
var self: Self = undefined;
|
||||
self.headerPtr().* = .{
|
||||
.len = data_offset + @sizeOf(T),
|
||||
.level = args.level,
|
||||
.@"type" = args.@"type",
|
||||
};
|
||||
self.dataPtr().* = args.data;
|
||||
return self;
|
||||
}
|
||||
|
||||
// TODO: include this version if we submit a PR to add this to std
|
||||
pub fn initNoData(args: struct {
|
||||
level: c_int,
|
||||
@"type": c_int,
|
||||
}) Self {
|
||||
var self: Self = undefined;
|
||||
self.headerPtr().* = .{
|
||||
.len = data_offset + @sizeOf(T),
|
||||
.level = args.level,
|
||||
.@"type" = args.@"type",
|
||||
};
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn headerPtr(self: *Self) *Header {
|
||||
return @ptrCast(*Header, self);
|
||||
}
|
||||
pub fn dataPtr(self: *Self) *align(data_align) T {
|
||||
return @ptrCast(*T, self.bytes[data_offset..]);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test {
|
||||
std.testing.refAllDecls(Cmsghdr([3]std.os.fd_t));
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
const std = @import("std");
|
||||
const testing = std.testing;
|
||||
const net = std.net;
|
||||
const fmt = std.fmt;
|
||||
const mem = std.mem;
|
||||
const print = std.debug.print;
|
||||
|
||||
|
||||
fn print_current_dir() !void {
|
||||
const buffer_size = 10000;
|
||||
var buffer: [buffer_size]u8 = undefined;
|
||||
var fba = std.heap.FixedBufferAllocator.init(&buffer);
|
||||
var allocator = fba.allocator();
|
||||
|
||||
print("Print current directory\n", .{});
|
||||
var current_dir = try std.fs.cwd().openIterableDir(".", .{});
|
||||
var walker = try current_dir.walk(allocator);
|
||||
while (try walker.next()) |entry| {
|
||||
print("content: {s}\n", .{entry.basename});
|
||||
}
|
||||
walker.deinit();
|
||||
print("DONE\n", .{});
|
||||
}
|
||||
|
||||
fn add_line_in_file() !void {
|
||||
var cwd = std.fs.cwd();
|
||||
var f = try cwd.createFile("some-file.log", .{.read = true});
|
||||
// defer f.close(); // closed in add_line_from_fd
|
||||
|
||||
var writer = f.writer();
|
||||
|
||||
try writer.print("hello\n", .{});
|
||||
|
||||
try add_line_from_fd(f.handle);
|
||||
}
|
||||
|
||||
fn add_line_from_fd(fd: i32) !void {
|
||||
// var f = std.fs.File {.handle = fd};
|
||||
// defer f.close();
|
||||
|
||||
_ = try std.os.write(fd, "hello this is another line\n");
|
||||
std.os.close(fd);
|
||||
}
|
||||
|
||||
pub fn main() !u8 {
|
||||
// var path = "/tmp/.TEST_USOCK";
|
||||
// print("Opening the file '{s}'.\n", .{path});
|
||||
//try print_current_dir();
|
||||
try add_line_in_file();
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
const std = @import("std");
|
||||
const hexdump = @import("./hexdump.zig");
|
||||
const testing = std.testing;
|
||||
const net = std.net;
|
||||
const fmt = std.fmt;
|
||||
|
||||
const Timer = std.time.Timer;
|
||||
|
||||
const print = std.debug.print;
|
||||
const P = std.ArrayList(std.os.pollfd);
|
||||
|
||||
fn arraylist_test() !void {
|
||||
const config = .{.safety = true};
|
||||
var gpa = std.heap.GeneralPurposeAllocator(config){};
|
||||
defer _ = gpa.deinit();
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
var p = P.init(allocator);
|
||||
defer p.deinit();
|
||||
|
||||
try p.append(.{.fd = 8, .events = 0, .revents = 0});
|
||||
|
||||
for(p.items) |i| { print("fd: {}\n", .{i.fd}); }
|
||||
}
|
||||
|
||||
fn timer_test() !void {
|
||||
var timer = try Timer.start();
|
||||
|
||||
var count: u64 = 0;
|
||||
while (count < 100000) {
|
||||
count += 1;
|
||||
print("\rcount = {}", .{count});
|
||||
}
|
||||
print("\n", .{});
|
||||
|
||||
var duration = timer.read();
|
||||
print("took {} us\n", .{duration / 1000});
|
||||
}
|
||||
|
||||
pub fn main() !u8 {
|
||||
// try arraylist_test();
|
||||
try timer_test();
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
const std = @import("std");
|
||||
const hexdump = @import("./hexdump.zig");
|
||||
const testing = std.testing;
|
||||
const net = std.net;
|
||||
const os = std.os;
|
||||
const fmt = std.fmt;
|
||||
|
||||
const print = std.debug.print;
|
||||
|
||||
// const config = .{.safety = true};
|
||||
// var gpa = std.heap.GeneralPurposeAllocator(config){};
|
||||
// defer _ = gpa.deinit();
|
||||
// const allocator = gpa.allocator();
|
||||
|
||||
fn disconnect(stream: *net.StreamServer) void { stream.close(); }
|
||||
|
||||
fn server_init() net.StreamServer {
|
||||
// no reuse_address and default kernel_backlog
|
||||
return net.StreamServer.init(.{});
|
||||
}
|
||||
|
||||
fn remove_unix_socket(path: []const u8) void {
|
||||
std.fs.deleteFileAbsolute(path) catch |err| switch(err) {
|
||||
else => { print("error: {}\n", .{err}); }
|
||||
};
|
||||
}
|
||||
|
||||
fn waiting_for_connection(stream: *net.StreamServer
|
||||
, path: []const u8) !net.StreamServer.Connection {
|
||||
var address = try net.Address.initUnix(path);
|
||||
try stream.listen(address);
|
||||
|
||||
// const linux = os.linux;
|
||||
// var tfd = try os.timerfd_create(linux.CLOCK.MONOTONIC, linux.TFD.CLOEXEC);
|
||||
// defer os.close(tfd);
|
||||
// // Fire event 10_000_000ns = 10s after the os.timerfd_settime call.
|
||||
// var sit: linux.itimerspec = .{ .it_interval = .{ .tv_sec = 0, .tv_nsec = 0 }
|
||||
// , .it_value = .{ .tv_sec = 0, .tv_nsec = 10 * (1000 * 1000 * 1000) } };
|
||||
// print("before os.timerfd_settime\n", .{});
|
||||
// try os.timerfd_settime(tfd, 0, &sit, null);
|
||||
|
||||
// var fds: [2]os.pollfd =
|
||||
// .{ .{ .fd = tfd, .events = os.linux.POLL.IN, .revents = 0 }
|
||||
// , .{ .fd = lfd, .events = os.linux.POLL.IN, .revents = 0 }};
|
||||
// var count = try os.poll(&fds, -1); // -1 => infinite waiting
|
||||
|
||||
var waiting_duration: i32 = 10 * 1000; // in ms
|
||||
print("waiting for 10 seconds, tops\n", .{});
|
||||
var ssockfd = stream.sockfd; // actual listener (server)
|
||||
var lfd: os.socket_t = undefined;
|
||||
|
||||
if (ssockfd) |sfd| { lfd = sfd; }
|
||||
else { return error.Undefined; }
|
||||
|
||||
print("Let's wait for an event (either stdin or unix socket)\n", .{});
|
||||
var count: usize = undefined;
|
||||
while(true) {
|
||||
var fds: [2]os.pollfd =
|
||||
.{.{ .fd = 0, .events = os.linux.POLL.IN, .revents = 0 }
|
||||
, .{ .fd = lfd, .events = os.linux.POLL.IN, .revents = 0 }};
|
||||
print("fds: {any}\n", .{fds});
|
||||
count = try os.poll(&fds, waiting_duration);
|
||||
if (count == 0) { print("no client, still waiting\n", .{}); continue; }
|
||||
print("fds NOW: {any}\n", .{fds});
|
||||
break;
|
||||
}
|
||||
|
||||
return stream.accept();
|
||||
}
|
||||
|
||||
fn receive_msg(stream: net.Stream) !void {
|
||||
var buffer: [1000]u8 = undefined;
|
||||
var fbs = std.io.fixedBufferStream(&buffer);
|
||||
var reader = fbs.reader();
|
||||
|
||||
_ = try stream.read(buffer[0..]);
|
||||
|
||||
const msg_type = try reader.readByte();
|
||||
const msg_len = try reader.readIntBig(u32);
|
||||
const msg_payload = buffer[5..5+msg_len];
|
||||
print ("type: {}, len {}, content: {s}\n"
|
||||
, .{msg_type, msg_len, msg_payload});
|
||||
}
|
||||
|
||||
pub fn main() !u8 {
|
||||
var path = "/tmp/.TEST_USOCK";
|
||||
print("Init UNIX server to {s}...\n", .{path});
|
||||
var stream = server_init();
|
||||
defer stream.deinit();
|
||||
defer remove_unix_socket(path);
|
||||
|
||||
// TODO
|
||||
print("Waiting for a connection...\n", .{});
|
||||
var connection = try waiting_for_connection(&stream, path);
|
||||
print("Someone is connected! Receiving a message...\n", .{});
|
||||
try receive_msg(connection.stream);
|
||||
|
||||
print("Disconnection...\n", .{});
|
||||
disconnect(&stream);
|
||||
print("Disconnected!\n", .{});
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue