Parcourir la source

Restructure and inotail

tags/v0.0.62
Peter Vivell il y a 7 ans
Parent
révision
5a62c8f2bc

+ 7
- 5
Makefile Voir le fichier

@@ -13,9 +13,11 @@ all: $(addsuffix -all,$(SUBDIRS))
13 13
 install-bin: all
14 14
 	$(INSTALL) -m 755 -d    $(DESTDIR)/usr/bin
15 15
 	$(INSTALL) -m 755       src/radangel $(DESTDIR)/usr/bin
16
-	$(INSTALL) -m 755       src/contrib/websockify $(DESTDIR)/usr/bin
16
+	$(INSTALL) -m 755       src/websockify/websockify $(DESTDIR)/usr/bin
17
+	$(INSTALL) -m 755       src/inotail/inotail $(DESTDIR)/usr/bin
17 18
 	$(STRIP)   -s           $(DESTDIR)/usr/bin/radangel
18 19
 	$(STRIP)   -s           $(DESTDIR)/usr/bin/websockify
20
+	$(STRIP)   -s           $(DESTDIR)/usr/bin/inotail
19 21
 
20 22
 uninstall-bin:
21 23
 	rm -f $(DESTDIR)/usr/bin/radangel
@@ -38,16 +40,16 @@ install-config:
38 40
 	$(INSTALL) -m 644       service/radangel.service $(DESTDIR)/lib/systemd/system
39 41
 	$(INSTALL) -m 644       service/websockify.service $(DESTDIR)/lib/systemd/system
40 42
 	$(INSTALL) -m 755 -d    $(DESTDIR)/etc/nginx/conf.d
41
-	$(INSTALL) -m 644       config/nginx-default-datasrc-location.conf $(DESTDIR)/etc/nginx/conf.d
43
+	$(INSTALL) -m 644       config/radangel.conf $(DESTDIR)/etc/nginx/conf.d
42 44
 	$(INSTALL) -m 755 -d    $(DESTDIR)/etc/udev/rules.d
43
-	$(INSTALL) -m 644       config/60-kromek-radangel.rules $(DESTDIR)/etc/udev/rules.d
45
+	$(INSTALL) -m 644       config/60-radangel.rules $(DESTDIR)/etc/udev/rules.d
44 46
 
45 47
 uninstall-config:
46 48
 	rm -f $(DESTDIR)/lib/systemd/system/datasrc.service
47 49
 	rm -f $(DESTDIR)/lib/systemd/system/radangel.service
48 50
 	rm -f $(DESTDIR)/lib/systemd/system/websockify.service
49
-	rm -f $(DESTDIR)/etc/nginx/conf.d/nginx-default-datasrc-location.conf
50
-	rm -f $(DESTDIR)/etc/udev/rules.d/60-kromek-radangel.rules
51
+	rm -f $(DESTDIR)/etc/nginx/conf.d/radangel.conf
52
+	rm -f $(DESTDIR)/etc/udev/rules.d/60-radangel.rules
51 53
 
52 54
 install: install-bin install-html install-config
53 55
 

+ 1
- 0
config/60-radangel.rules Voir le fichier

@@ -0,0 +1 @@
1
+SUBSYSTEM=="usb", ATTR{idVendor}=="04d8", ATTR{idProduct}=="0100", MODE="0666"

+ 42
- 0
config/radangel.conf Voir le fichier

@@ -0,0 +1,42 @@
1
+server {
2
+	listen 81;
3
+	listen [::]:81;
4
+
5
+	root /usr/share/radangel/html;
6
+
7
+	index index.html index.htm;
8
+
9
+	server_name _;
10
+
11
+	location / {
12
+		try_files $uri $uri/ =404;
13
+	}
14
+
15
+	location /data/hist.tsv {
16
+		alias /tmp/stats.tsv;
17
+	}
18
+
19
+	location /data/stream.tsv { 
20
+		alias /tmp/event.tsv;
21
+	}
22
+
23
+	location /data/stream.ws {
24
+		proxy_http_version 1.1;
25
+		proxy_set_header Upgrade         $http_upgrade;
26
+		proxy_set_header Connection      "upgrade";
27
+
28
+		proxy_set_header Host            $host; # $http_host;
29
+		proxy_set_header X-Real-IP       $remote_addr;
30
+		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
31
+		proxy_set_header X-NginX-Proxy   true;
32
+
33
+		proxy_read_timeout 3600;
34
+		proxy_buffering off;
35
+		tcp_nodelay on;
36
+
37
+		error_page 502 = /data/stream.tsv; # serve http for non-ws clients
38
+
39
+		proxy_pass http://127.0.0.1:5800/websockify;
40
+	}
41
+
42
+}

+ 2
- 2
debian/control Voir le fichier

@@ -4,11 +4,11 @@ Priority: optional
4 4
 Maintainer: Peter Vivell <peter@vivell.de>
5 5
 Build-Depends: debhelper (>= 10)
6 6
 Standards-Version: 4.1.2
7
-Homepage: https://git.unino.de/pvivell/radangel/wiki
7
+Homepage: https://git.unino.de/pvivell/radangel
8 8
 #Vcs-Git: https://git.unino.de/pvivell/radangel.git
9 9
 #Vcs-Browser: https://git.unino.de/pvivell/radangel
10 10
 
11 11
 Package: radangel
12 12
 Architecture: amd64 arm64 armel armhf i386 mips mips64el mipsel powerpc ppc64el s390x
13
-Depends: ${shlibs:Depends}, ${misc:Depends}
13
+Depends: ${shlibs:Depends}, ${misc:Depends} socat nginx
14 14
 Description: Kromek Radangel gamma spectrometer daemon.

+ 1
- 1
service/radangel.service Voir le fichier

@@ -21,7 +21,7 @@ ExecStartPre=/bin/chmod o+r ${EVENT_FILE}
21 21
 ExecStartPre=/bin/chmod o+r ${STATS_FILE}
22 22
 
23 23
 WorkingDirectory=/opt/radangel
24
-ExecStart=/opt/radangel/radangel --verbose \
24
+ExecStart=/usr/bin/radangel --verbose \
25 25
 	--event ${EVENT_FILE}              \
26 26
 	--stats ${STATS_FILE}
27 27
 

+ 1
- 1
service/websockify.service Voir le fichier

@@ -20,7 +20,7 @@ ExecStartPre=/bin/sh -c '\
20 20
 	touch /var/run/websockify.pid; \
21 21
 	chown www-data:www-data /var/run/websockify.pid'
22 22
 WorkingDirectory=/tmp
23
-ExecStart=/opt/websockify/websockify --daemon --pid /var/run/websockify.pid \
23
+ExecStart=/usr/bin/websockify --daemon --pid /var/run/websockify.pid \
24 24
 	${LISTEN_HOST}:${LISTEN_PORT} \
25 25
 	${TARGET_HOST}:${TARGET_PORT}
26 26
 

+ 7
- 0
src/inotail/.gitignore Voir le fichier

@@ -0,0 +1,7 @@
1
+# binary files
2
+*.o
3
+inotail
4
+inotify-watchdir
5
+
6
+# other
7
+cscope.out

+ 339
- 0
src/inotail/LICENSE Voir le fichier

@@ -0,0 +1,339 @@
1
+		    GNU GENERAL PUBLIC LICENSE
2
+		       Version 2, June 1991
3
+
4
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6
+ Everyone is permitted to copy and distribute verbatim copies
7
+ of this license document, but changing it is not allowed.
8
+
9
+			    Preamble
10
+
11
+  The licenses for most software are designed to take away your
12
+freedom to share and change it.  By contrast, the GNU General Public
13
+License is intended to guarantee your freedom to share and change free
14
+software--to make sure the software is free for all its users.  This
15
+General Public License applies to most of the Free Software
16
+Foundation's software and to any other program whose authors commit to
17
+using it.  (Some other Free Software Foundation software is covered by
18
+the GNU Lesser General Public License instead.)  You can apply it to
19
+your programs, too.
20
+
21
+  When we speak of free software, we are referring to freedom, not
22
+price.  Our General Public Licenses are designed to make sure that you
23
+have the freedom to distribute copies of free software (and charge for
24
+this service if you wish), that you receive source code or can get it
25
+if you want it, that you can change the software or use pieces of it
26
+in new free programs; and that you know you can do these things.
27
+
28
+  To protect your rights, we need to make restrictions that forbid
29
+anyone to deny you these rights or to ask you to surrender the rights.
30
+These restrictions translate to certain responsibilities for you if you
31
+distribute copies of the software, or if you modify it.
32
+
33
+  For example, if you distribute copies of such a program, whether
34
+gratis or for a fee, you must give the recipients all the rights that
35
+you have.  You must make sure that they, too, receive or can get the
36
+source code.  And you must show them these terms so they know their
37
+rights.
38
+
39
+  We protect your rights with two steps: (1) copyright the software, and
40
+(2) offer you this license which gives you legal permission to copy,
41
+distribute and/or modify the software.
42
+
43
+  Also, for each author's protection and ours, we want to make certain
44
+that everyone understands that there is no warranty for this free
45
+software.  If the software is modified by someone else and passed on, we
46
+want its recipients to know that what they have is not the original, so
47
+that any problems introduced by others will not reflect on the original
48
+authors' reputations.
49
+
50
+  Finally, any free program is threatened constantly by software
51
+patents.  We wish to avoid the danger that redistributors of a free
52
+program will individually obtain patent licenses, in effect making the
53
+program proprietary.  To prevent this, we have made it clear that any
54
+patent must be licensed for everyone's free use or not licensed at all.
55
+
56
+  The precise terms and conditions for copying, distribution and
57
+modification follow.
58
+
59
+		    GNU GENERAL PUBLIC LICENSE
60
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61
+
62
+  0. This License applies to any program or other work which contains
63
+a notice placed by the copyright holder saying it may be distributed
64
+under the terms of this General Public License.  The "Program", below,
65
+refers to any such program or work, and a "work based on the Program"
66
+means either the Program or any derivative work under copyright law:
67
+that is to say, a work containing the Program or a portion of it,
68
+either verbatim or with modifications and/or translated into another
69
+language.  (Hereinafter, translation is included without limitation in
70
+the term "modification".)  Each licensee is addressed as "you".
71
+
72
+Activities other than copying, distribution and modification are not
73
+covered by this License; they are outside its scope.  The act of
74
+running the Program is not restricted, and the output from the Program
75
+is covered only if its contents constitute a work based on the
76
+Program (independent of having been made by running the Program).
77
+Whether that is true depends on what the Program does.
78
+
79
+  1. You may copy and distribute verbatim copies of the Program's
80
+source code as you receive it, in any medium, provided that you
81
+conspicuously and appropriately publish on each copy an appropriate
82
+copyright notice and disclaimer of warranty; keep intact all the
83
+notices that refer to this License and to the absence of any warranty;
84
+and give any other recipients of the Program a copy of this License
85
+along with the Program.
86
+
87
+You may charge a fee for the physical act of transferring a copy, and
88
+you may at your option offer warranty protection in exchange for a fee.
89
+
90
+  2. You may modify your copy or copies of the Program or any portion
91
+of it, thus forming a work based on the Program, and copy and
92
+distribute such modifications or work under the terms of Section 1
93
+above, provided that you also meet all of these conditions:
94
+
95
+    a) You must cause the modified files to carry prominent notices
96
+    stating that you changed the files and the date of any change.
97
+
98
+    b) You must cause any work that you distribute or publish, that in
99
+    whole or in part contains or is derived from the Program or any
100
+    part thereof, to be licensed as a whole at no charge to all third
101
+    parties under the terms of this License.
102
+
103
+    c) If the modified program normally reads commands interactively
104
+    when run, you must cause it, when started running for such
105
+    interactive use in the most ordinary way, to print or display an
106
+    announcement including an appropriate copyright notice and a
107
+    notice that there is no warranty (or else, saying that you provide
108
+    a warranty) and that users may redistribute the program under
109
+    these conditions, and telling the user how to view a copy of this
110
+    License.  (Exception: if the Program itself is interactive but
111
+    does not normally print such an announcement, your work based on
112
+    the Program is not required to print an announcement.)
113
+
114
+These requirements apply to the modified work as a whole.  If
115
+identifiable sections of that work are not derived from the Program,
116
+and can be reasonably considered independent and separate works in
117
+themselves, then this License, and its terms, do not apply to those
118
+sections when you distribute them as separate works.  But when you
119
+distribute the same sections as part of a whole which is a work based
120
+on the Program, the distribution of the whole must be on the terms of
121
+this License, whose permissions for other licensees extend to the
122
+entire whole, and thus to each and every part regardless of who wrote it.
123
+
124
+Thus, it is not the intent of this section to claim rights or contest
125
+your rights to work written entirely by you; rather, the intent is to
126
+exercise the right to control the distribution of derivative or
127
+collective works based on the Program.
128
+
129
+In addition, mere aggregation of another work not based on the Program
130
+with the Program (or with a work based on the Program) on a volume of
131
+a storage or distribution medium does not bring the other work under
132
+the scope of this License.
133
+
134
+  3. You may copy and distribute the Program (or a work based on it,
135
+under Section 2) in object code or executable form under the terms of
136
+Sections 1 and 2 above provided that you also do one of the following:
137
+
138
+    a) Accompany it with the complete corresponding machine-readable
139
+    source code, which must be distributed under the terms of Sections
140
+    1 and 2 above on a medium customarily used for software interchange; or,
141
+
142
+    b) Accompany it with a written offer, valid for at least three
143
+    years, to give any third party, for a charge no more than your
144
+    cost of physically performing source distribution, a complete
145
+    machine-readable copy of the corresponding source code, to be
146
+    distributed under the terms of Sections 1 and 2 above on a medium
147
+    customarily used for software interchange; or,
148
+
149
+    c) Accompany it with the information you received as to the offer
150
+    to distribute corresponding source code.  (This alternative is
151
+    allowed only for noncommercial distribution and only if you
152
+    received the program in object code or executable form with such
153
+    an offer, in accord with Subsection b above.)
154
+
155
+The source code for a work means the preferred form of the work for
156
+making modifications to it.  For an executable work, complete source
157
+code means all the source code for all modules it contains, plus any
158
+associated interface definition files, plus the scripts used to
159
+control compilation and installation of the executable.  However, as a
160
+special exception, the source code distributed need not include
161
+anything that is normally distributed (in either source or binary
162
+form) with the major components (compiler, kernel, and so on) of the
163
+operating system on which the executable runs, unless that component
164
+itself accompanies the executable.
165
+
166
+If distribution of executable or object code is made by offering
167
+access to copy from a designated place, then offering equivalent
168
+access to copy the source code from the same place counts as
169
+distribution of the source code, even though third parties are not
170
+compelled to copy the source along with the object code.
171
+
172
+  4. You may not copy, modify, sublicense, or distribute the Program
173
+except as expressly provided under this License.  Any attempt
174
+otherwise to copy, modify, sublicense or distribute the Program is
175
+void, and will automatically terminate your rights under this License.
176
+However, parties who have received copies, or rights, from you under
177
+this License will not have their licenses terminated so long as such
178
+parties remain in full compliance.
179
+
180
+  5. You are not required to accept this License, since you have not
181
+signed it.  However, nothing else grants you permission to modify or
182
+distribute the Program or its derivative works.  These actions are
183
+prohibited by law if you do not accept this License.  Therefore, by
184
+modifying or distributing the Program (or any work based on the
185
+Program), you indicate your acceptance of this License to do so, and
186
+all its terms and conditions for copying, distributing or modifying
187
+the Program or works based on it.
188
+
189
+  6. Each time you redistribute the Program (or any work based on the
190
+Program), the recipient automatically receives a license from the
191
+original licensor to copy, distribute or modify the Program subject to
192
+these terms and conditions.  You may not impose any further
193
+restrictions on the recipients' exercise of the rights granted herein.
194
+You are not responsible for enforcing compliance by third parties to
195
+this License.
196
+
197
+  7. If, as a consequence of a court judgment or allegation of patent
198
+infringement or for any other reason (not limited to patent issues),
199
+conditions are imposed on you (whether by court order, agreement or
200
+otherwise) that contradict the conditions of this License, they do not
201
+excuse you from the conditions of this License.  If you cannot
202
+distribute so as to satisfy simultaneously your obligations under this
203
+License and any other pertinent obligations, then as a consequence you
204
+may not distribute the Program at all.  For example, if a patent
205
+license would not permit royalty-free redistribution of the Program by
206
+all those who receive copies directly or indirectly through you, then
207
+the only way you could satisfy both it and this License would be to
208
+refrain entirely from distribution of the Program.
209
+
210
+If any portion of this section is held invalid or unenforceable under
211
+any particular circumstance, the balance of the section is intended to
212
+apply and the section as a whole is intended to apply in other
213
+circumstances.
214
+
215
+It is not the purpose of this section to induce you to infringe any
216
+patents or other property right claims or to contest validity of any
217
+such claims; this section has the sole purpose of protecting the
218
+integrity of the free software distribution system, which is
219
+implemented by public license practices.  Many people have made
220
+generous contributions to the wide range of software distributed
221
+through that system in reliance on consistent application of that
222
+system; it is up to the author/donor to decide if he or she is willing
223
+to distribute software through any other system and a licensee cannot
224
+impose that choice.
225
+
226
+This section is intended to make thoroughly clear what is believed to
227
+be a consequence of the rest of this License.
228
+
229
+  8. If the distribution and/or use of the Program is restricted in
230
+certain countries either by patents or by copyrighted interfaces, the
231
+original copyright holder who places the Program under this License
232
+may add an explicit geographical distribution limitation excluding
233
+those countries, so that distribution is permitted only in or among
234
+countries not thus excluded.  In such case, this License incorporates
235
+the limitation as if written in the body of this License.
236
+
237
+  9. The Free Software Foundation may publish revised and/or new versions
238
+of the General Public License from time to time.  Such new versions will
239
+be similar in spirit to the present version, but may differ in detail to
240
+address new problems or concerns.
241
+
242
+Each version is given a distinguishing version number.  If the Program
243
+specifies a version number of this License which applies to it and "any
244
+later version", you have the option of following the terms and conditions
245
+either of that version or of any later version published by the Free
246
+Software Foundation.  If the Program does not specify a version number of
247
+this License, you may choose any version ever published by the Free Software
248
+Foundation.
249
+
250
+  10. If you wish to incorporate parts of the Program into other free
251
+programs whose distribution conditions are different, write to the author
252
+to ask for permission.  For software which is copyrighted by the Free
253
+Software Foundation, write to the Free Software Foundation; we sometimes
254
+make exceptions for this.  Our decision will be guided by the two goals
255
+of preserving the free status of all derivatives of our free software and
256
+of promoting the sharing and reuse of software generally.
257
+
258
+			    NO WARRANTY
259
+
260
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
262
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
266
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
267
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268
+REPAIR OR CORRECTION.
269
+
270
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278
+POSSIBILITY OF SUCH DAMAGES.
279
+
280
+		     END OF TERMS AND CONDITIONS
281
+
282
+	    How to Apply These Terms to Your New Programs
283
+
284
+  If you develop a new program, and you want it to be of the greatest
285
+possible use to the public, the best way to achieve this is to make it
286
+free software which everyone can redistribute and change under these terms.
287
+
288
+  To do so, attach the following notices to the program.  It is safest
289
+to attach them to the start of each source file to most effectively
290
+convey the exclusion of warranty; and each file should have at least
291
+the "copyright" line and a pointer to where the full notice is found.
292
+
293
+    <one line to give the program's name and a brief idea of what it does.>
294
+    Copyright (C) <year>  <name of author>
295
+
296
+    This program is free software; you can redistribute it and/or modify
297
+    it under the terms of the GNU General Public License as published by
298
+    the Free Software Foundation; either version 2 of the License, or
299
+    (at your option) any later version.
300
+
301
+    This program is distributed in the hope that it will be useful,
302
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
303
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
304
+    GNU General Public License for more details.
305
+
306
+    You should have received a copy of the GNU General Public License along
307
+    with this program; if not, write to the Free Software Foundation, Inc.,
308
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309
+
310
+Also add information on how to contact you by electronic and paper mail.
311
+
312
+If the program is interactive, make it output a short notice like this
313
+when it starts in an interactive mode:
314
+
315
+    Gnomovision version 69, Copyright (C) year name of author
316
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317
+    This is free software, and you are welcome to redistribute it
318
+    under certain conditions; type `show c' for details.
319
+
320
+The hypothetical commands `show w' and `show c' should show the appropriate
321
+parts of the General Public License.  Of course, the commands you use may
322
+be called something other than `show w' and `show c'; they could even be
323
+mouse-clicks or menu items--whatever suits your program.
324
+
325
+You should also get your employer (if you work as a programmer) or your
326
+school, if any, to sign a "copyright disclaimer" for the program, if
327
+necessary.  Here is a sample; alter the names:
328
+
329
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
331
+
332
+  <signature of Ty Coon>, 1 April 1989
333
+  Ty Coon, President of Vice
334
+
335
+This General Public License does not permit incorporating your program into
336
+proprietary programs.  If your program is a subroutine library, you may
337
+consider it more useful to permit linking proprietary applications with the
338
+library.  If this is what you want to do, use the GNU Lesser General
339
+Public License instead of this License.

+ 47
- 0
src/inotail/Makefile Voir le fichier

@@ -0,0 +1,47 @@
1
+# Makefile for inotail
2
+#
3
+# Copyright (C) 2006-2007 Tobias Klauser <tklauser@distanz.ch>
4
+#
5
+# Licensed under the terms of the GNU General Public License; version 2 or later.
6
+
7
+VERSION	= 0.5
8
+
9
+# Paths
10
+prefix	= /usr/local
11
+BINDIR	= $(prefix)/bin
12
+MANDIR	= $(prefix)/share/man/man1
13
+
14
+CC	:= gcc
15
+CFLAGS	:= $(CFLAGS) -pipe -D_USE_SOURCE -DVERSION="\"$(VERSION)\"" -W -Wall \
16
+	   -Wstrict-prototypes -Wsign-compare -Wshadow -Wchar-subscripts \
17
+	   -Wmissing-declarations -Wpointer-arith -Wcast-align -Wmissing-prototypes
18
+
19
+# Compile with 'make DEBUG=true' to enable debugging
20
+DEBUG = false
21
+ifeq ($(strip $(DEBUG)),true)
22
+	CFLAGS  += -g -DDEBUG
23
+endif
24
+
25
+all: inotail
26
+inotail: inotail.o
27
+
28
+%.o: %.c %.h
29
+	$(CC) $(CFLAGS) -c $< -o $@
30
+
31
+install: inotail
32
+	install -m 775 -D inotail $(BINDIR)/inotail
33
+	install -m 644 -D inotail.1 $(MANDIR)/inotail.1
34
+	gzip -9 $(MANDIR)/inotail.1
35
+
36
+uninstall:
37
+	rm $(BINDIR)/inotail $(MANDIR)/inotail.1*
38
+
39
+cscope:
40
+	cscope -b
41
+
42
+release:
43
+	git-archive --format=tar --prefix=inotail-$(VERSION)/ HEAD | gzip -9v > ../inotail-$(VERSION).tar.gz
44
+	git-archive --format=tar --prefix=inotail-$(VERSION)/ HEAD | bzip2 -9v > ../inotail-$(VERSION).tar.bz2
45
+
46
+clean:
47
+	rm -f inotail *.o cscope.*

+ 46
- 0
src/inotail/README Voir le fichier

@@ -0,0 +1,46 @@
1
+ inotail - inotify enhanced tail
2
+o===============================o
3
+
4
+inotail is a replacement for the 'tail' program found in the base installation
5
+of every Linux/UNIX system. It makes use of the inotify infrastructure in recent
6
+versions of the Linux kernel to speed up tailing files in the follow mode (the
7
+'-f' option). Standard tail polls the file every second by default while inotail
8
+listens to special events sent by the kernel through the inotify API to
9
+determine whether a file needs to be reread.
10
+
11
+Currently inotail is not fully compatible to neither POSIX or GNU tail but might
12
+be in the future.
13
+
14
+Requirements
15
+------------
16
+- Linux kernel 2.6.13 or higher with CONFIG_INOTIFY enabled
17
+- Standard C Library (tested with GNU libc but might work with others too)
18
+- GCC (other compilers might work but are not tested)
19
+
20
+Building and installing inotail
21
+-------------------------------
22
+To build inotail type:
23
+
24
+	$ make
25
+
26
+By default, inotail is installed to /usr/local/bin/, the manpage is installed to
27
+/usr/local/share/man/man1/. To install the inotail files to these locations type:
28
+
29
+	$ make install
30
+
31
+To change these locations just set the prefix variable. E.g. to install the
32
+inotail binary to /usr/ and the manpage to /usr/share/man/ respectively type:
33
+
34
+	$ make prefix=/usr install
35
+
36
+License
37
+-------
38
+inotail is licensed under the terms of the GNU General Public License version 2
39
+or later. You can find the full text in the file LICENSE in the source tree of
40
+inotail.
41
+
42
+The files inotify.h and inotify-syscalls.h were taken from the source tree of
43
+the Linux kernel and slightly altered. Both are licensed under the terms of the
44
+GNU General Public License version 2.
45
+
46
+-- Tobias Klauser <tklauser@distanz.ch>

+ 34
- 0
src/inotail/changelog Voir le fichier

@@ -0,0 +1,34 @@
1
+inotail 0.5
2
+
3
+  * Output verbose file headers correctly when used in a pipe
4
+  * Small code cleanups
5
+
6
+  -- Tobias Klauser <tklauser@distanz.ch> 2007-09-07 13:30
7
+
8
+inotail 0.4
9
+
10
+  * Use dynamic buffers of optimal size (st_blksize in struct stat) for
11
+    filesystem I/O (patch by Folkert van Heusden)
12
+  * Added handling of EINTR/EAGAIN while watching files for changes (patch by
13
+    Anthony Martinez)
14
+  * Better error checking and handling (patch by Folkert van Heusden)
15
+  * Various cleanups
16
+
17
+  -- Tobias Klauser <tklauser@distanz.ch> 2007-06-20 15:00
18
+
19
+inotail 0.3
20
+
21
+  * Follow files even if they were moved
22
+  * Fix a problem when tailing more than 4096 bytes/chars at once
23
+  * Only print the filename once when the -v option is specified
24
+  * Various small fixes and cleanups
25
+
26
+  -- Tobias Klauser <tklauser@distanz.ch> 2007-04-17 13:44
27
+
28
+inotail 0.2
29
+
30
+  * Support for the -n/-c +<num> options (tail relative to start of file)
31
+  * Copyright and license cleanup
32
+  * Various fixes
33
+
34
+  -- Tobias Klauser <tklauser@distanz.ch.ch> 2006-11-30 17:56

+ 59
- 0
src/inotail/inotail.1 Voir le fichier

@@ -0,0 +1,59 @@
1
+'\" t
2
+.\" ** The above line should force tbl to be a preprocessor **
3
+.\" Man page for inotail
4
+.\"
5
+.\" Copyright (c) 2006 Tobias Klauser <tklauser@distanz.ch>
6
+.\"
7
+.\" You may distribute under the terms of the GNU General Public
8
+.\" License as specified in the file COPYING that comes with
9
+.\" inotail.
10
+
11
+.pc
12
+.TH INOTAIL 1 "2006-08-13" "" "Inotify enhanced tail"
13
+.SH NAME
14
+inotail \- A fast and lightweight version of tail using inotify
15
+.SH SYNOPSIS
16
+.B inotail
17
+[OPTION]... [FILE]...
18
+.SH DESCRIPTION
19
+.B inotail
20
+is a replacement for the 'tail' program found in the base installation of every
21
+Linux/UNIX system. It makes use of the inotify infrastructure in recent versions
22
+of the Linux kernel to speed up tailing files in the follow mode (the '\-f'
23
+option). Standard tail polls the file every second by default while inotail
24
+listens to special events sent by the kernel through the inotify API to
25
+determine whether a file needs to be reread. \fINote:\fR inotail will not work
26
+on systems running a kernel without inotify. To enable inotify, please set
27
+CONFIG_INOTIFY=y in your Linux kernel configuration and recompile it.
28
+.PP
29
+Currently inotail is not fully compatible to neither POSIX or GNU tail but might
30
+be in the future.
31
+.SH OPTIONS
32
+.TP
33
+.B \-c \fIN\fR, \fB\-\-bytes\fR=\fIN\fR
34
+output the last N bytes. If the first character of N is a '+', begin printing
35
+with the Nth character from the start of each file.
36
+.TP
37
+.B \-f, \fB\-\-follow
38
+keep the file(s) open and print appended data as the file grows
39
+.TP
40
+.B \-n \fIN\fR, \fB\-\-lines\fR=\fIN\fR
41
+output the last N lines (default: 10) If the first character of N is a '+',
42
+begin printing with the Nth line from the start of each file.
43
+.TP
44
+.B \-v, \fB\-\-verbose
45
+print headers with file names
46
+.TP
47
+.B \-h, \fB\-\-help
48
+show help and exit
49
+.TP
50
+.B \-V, \fB\-\-version
51
+show inotail version and exit
52
+.SH AUTHOR
53
+.PP
54
+Written by Tobias Klauser
55
+<tklauser@distanz.ch>
56
+.SH SEE ALSO
57
+.PP
58
+.BR tail(1),
59
+.BR inotify(7)

+ 545
- 0
src/inotail/inotail.c Voir le fichier

@@ -0,0 +1,545 @@
1
+/*
2
+ * inotail.c
3
+ * A fast implementation of tail which uses the inotify API present in
4
+ * recent versions of the Linux kernel.
5
+ *
6
+ * Copyright (C) 2005-2007, Tobias Klauser <tklauser@distanz.ch>
7
+ *
8
+ * The idea was taken from turbotail.
9
+ *
10
+ * This program is free software; you can redistribute it and/or modify it under
11
+ * the terms of the GNU General Public License as published by the Free Software
12
+ * Foundation; either version 2, or (at your option) any later version.
13
+ *
14
+ * This program is distributed in the hope that it will be useful, but WITHOUT
15
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
17
+ * details.
18
+ *
19
+ * You should have received a copy of the GNU General Public License along with
20
+ * this program; if not, write to the Free Software Foundation, Inc., 51
21
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22
+ */
23
+
24
+#define _GNU_SOURCE
25
+
26
+#include <stdio.h>
27
+#include <stdlib.h>
28
+#include <string.h>
29
+#include <unistd.h>
30
+#include <fcntl.h>
31
+#include <errno.h>
32
+#include <getopt.h>
33
+#include <sys/types.h>
34
+#include <sys/stat.h>
35
+
36
+#include "inotify.h"
37
+#include "inotify-syscalls.h"
38
+
39
+#include "inotail.h"
40
+
41
+#define PROGRAM_NAME "inotail"
42
+#define DEFAULT_BUFFER_SIZE 4096
43
+/* inotify event buffer length for one file */
44
+#define INOTIFY_BUFLEN (4 * sizeof(struct inotify_event))
45
+
46
+/* Print header with filename before tailing the file? */
47
+static char verbose = 0;
48
+
49
+/* Tailing relative to begin or end of file */
50
+static char from_begin = 0;
51
+
52
+/* Number of ignored files */
53
+static int n_ignored = 0;
54
+
55
+/* Command line options */
56
+static const struct option long_opts[] = {
57
+	{ "bytes", required_argument, NULL, 'c' },
58
+	{ "follow", optional_argument, NULL, 'f' },
59
+	{ "lines", required_argument, NULL, 'n' },
60
+	{ "verbose", no_argument, NULL, 'v' },
61
+	{ "help", no_argument, NULL, 'h' },
62
+	{ "version", no_argument, NULL, 'V' },
63
+	{ NULL, 0, NULL, 0 }
64
+};
65
+
66
+static void *emalloc(size_t size)
67
+{
68
+	void *ret = malloc(size);
69
+
70
+	if (unlikely(!ret)) {
71
+		fprintf(stderr, "Error: Failed to allocate %d bytes of memory (%s)\n", (int) size, strerror(errno));
72
+		exit(EXIT_FAILURE);
73
+	}
74
+
75
+	return ret;
76
+}
77
+
78
+static void usage(const int status)
79
+{
80
+	fprintf(stdout, "Usage: %s [OPTION]... [FILE]...\n\n"
81
+			"  -c N, --bytes=N    output the last N bytes\n"
82
+			"  -f,   --follow     output as the file grows\n"
83
+			"  -n N, --lines=N    output the last N lines (default: %d)\n"
84
+			"  -v,   --verbose    print headers with file names\n"
85
+			"  -h,   --help       show this help and exit\n"
86
+			"  -V,   --version    show version and exit\n\n"
87
+			"If the first character of N (the number of bytes or lines) is a `+',\n"
88
+			"begin printing with the Nth item from the start of each file, otherwise,\n"
89
+			"print the last N items in the file.\n", PROGRAM_NAME, DEFAULT_N_LINES);
90
+
91
+	exit(status);
92
+}
93
+
94
+static inline void setup_file(struct file_struct *f)
95
+{
96
+	f->fd = f->i_watch = -1;
97
+	f->size = 0;
98
+	f->blksize = DEFAULT_BUFFER_SIZE;
99
+	f->ignore = 0;
100
+}
101
+
102
+static void ignore_file(struct file_struct *f)
103
+{
104
+	if (f->fd != -1) {
105
+		close(f->fd);
106
+		f->fd = -1;
107
+	}
108
+	f->ignore = 1;
109
+	n_ignored++;
110
+}
111
+
112
+static inline char *pretty_name(char *filename)
113
+{
114
+	return (strcmp(filename, "-") == 0) ? "standard input" : filename;
115
+}
116
+
117
+static void write_header(char *filename)
118
+{
119
+	static unsigned short first_file = 1;
120
+	static char *last = NULL;
121
+
122
+	if (last != filename) {
123
+		fprintf(stdout, "%s==> %s <==\n", (first_file ? "" : "\n"), pretty_name(filename));
124
+		fflush(stdout);		/* Make sure the header is printed before the content */
125
+	}
126
+
127
+	first_file = 0;
128
+	last = filename;
129
+}
130
+
131
+static off_t lines_to_offset_from_end(struct file_struct *f, unsigned long n_lines)
132
+{
133
+	off_t offset = f->size;
134
+	char *buf = emalloc(f->blksize);
135
+
136
+	n_lines++;	/* We also count the last \n */
137
+
138
+	while (offset > 0 && n_lines > 0) {
139
+		int i;
140
+		ssize_t rc, block_size = f->blksize;	/* Size of the current block we're reading */
141
+
142
+		if (offset < block_size)
143
+			block_size = offset;
144
+
145
+		/* Start of current block */
146
+		offset -= block_size;
147
+
148
+		if (lseek(f->fd, offset, SEEK_SET) == (off_t) -1) {
149
+			fprintf(stderr, "Error: Could not seek in file '%s' (%s)\n", f->name, strerror(errno));
150
+			free(buf);
151
+			return -1;
152
+		}
153
+
154
+		rc = read(f->fd, buf, block_size);
155
+		if (unlikely(rc < 0)) {
156
+			fprintf(stderr, "Error: Could not read from file '%s' (%s)\n", f->name, strerror(errno));
157
+			free(buf);
158
+			return -1;
159
+		}
160
+
161
+		for (i = block_size - 1; i > 0; i--) {
162
+			if (buf[i] == '\n') {
163
+				if (--n_lines == 0) {
164
+					free(buf);
165
+					return offset += i + 1; /* We don't want the first \n */
166
+				}
167
+			}
168
+		}
169
+	}
170
+
171
+	free(buf);
172
+	return offset;
173
+}
174
+
175
+static off_t lines_to_offset_from_begin(struct file_struct *f, unsigned long n_lines)
176
+{
177
+	char *buf;
178
+	off_t offset = 0;
179
+
180
+	/* tail everything for 'inotail -n +0' */
181
+	if (n_lines == 0)
182
+		return 0;
183
+
184
+	n_lines--;
185
+	buf = emalloc(f->blksize);
186
+
187
+	while (offset <= f->size && n_lines > 0) {
188
+		int i;
189
+		ssize_t rc, block_size = f->blksize;
190
+
191
+		if (lseek(f->fd, offset, SEEK_SET) == (off_t) -1) {
192
+			fprintf(stderr, "Error: Could not seek in file '%s' (%s)\n", f->name, strerror(errno));
193
+			free(buf);
194
+			return -1;
195
+		}
196
+
197
+		rc = read(f->fd, buf, block_size);
198
+		if (unlikely(rc < 0)) {
199
+			fprintf(stderr, "Error: Could not read from file '%s' (%s)\n", f->name, strerror(errno));
200
+			free(buf);
201
+			return -1;
202
+		} else if (rc < block_size)
203
+			block_size = rc;
204
+
205
+		for (i = 0; i < block_size; i++) {
206
+			if (buf[i] == '\n') {
207
+				if (--n_lines == 0) {
208
+					free(buf);
209
+					return offset + i + 1;
210
+				}
211
+			}
212
+		}
213
+
214
+		offset += block_size;
215
+	}
216
+
217
+	free(buf);
218
+	return offset;
219
+}
220
+
221
+static off_t lines_to_offset(struct file_struct *f, unsigned long n_lines)
222
+{
223
+	if (from_begin)
224
+		return lines_to_offset_from_begin(f, n_lines);
225
+	else
226
+		return lines_to_offset_from_end(f, n_lines);
227
+}
228
+
229
+static off_t bytes_to_offset(struct file_struct *f, unsigned long n_bytes)
230
+{
231
+	off_t offset = 0;
232
+
233
+	/* tail everything for 'inotail -c +0' */
234
+	if (from_begin) {
235
+		if (n_bytes > 0)
236
+			offset = (off_t) n_bytes - 1;
237
+	} else if ((off_t) n_bytes < f->size)
238
+		offset = f->size - (off_t) n_bytes;
239
+
240
+	return offset;
241
+}
242
+
243
+static ssize_t tail_pipe(struct file_struct *f)
244
+{
245
+	ssize_t rc;
246
+	char *buf = emalloc(f->blksize);
247
+
248
+	if (verbose)
249
+		write_header(f->name);
250
+
251
+	/* We will just tail everything here */
252
+	while ((rc = read(f->fd, buf, f->blksize)) > 0) {
253
+		if (write(STDOUT_FILENO, buf, (size_t) rc) <= 0) {
254
+			/* e.g. when writing to a pipe which gets closed */
255
+			fprintf(stderr, "Error: Could not write to stdout (%s)\n", strerror(errno));
256
+			rc = -1;
257
+			break;
258
+		}
259
+	}
260
+
261
+	free(buf);
262
+	return rc;
263
+}
264
+
265
+static int tail_file(struct file_struct *f, unsigned long n_units, char mode, char forever)
266
+{
267
+	ssize_t bytes_read = 0;
268
+	off_t offset = 0;
269
+	char *buf;
270
+	struct stat finfo;
271
+
272
+	if (strcmp(f->name, "-") == 0)
273
+		f->fd = STDIN_FILENO;
274
+	else {
275
+		f->fd = open(f->name, O_RDONLY);
276
+		if (unlikely(f->fd < 0)) {
277
+			fprintf(stderr, "Error: Could not open file '%s' (%s)\n", f->name, strerror(errno));
278
+			ignore_file(f);
279
+			return -1;
280
+		}
281
+	}
282
+
283
+	if (fstat(f->fd, &finfo) < 0) {
284
+		fprintf(stderr, "Error: Could not stat file '%s' (%s)\n", f->name, strerror(errno));
285
+		ignore_file(f);
286
+		return -1;
287
+	}
288
+
289
+	if (!IS_TAILABLE(finfo.st_mode)) {
290
+		fprintf(stderr, "Error: '%s' of unsupported file type (%s)\n", f->name, strerror(errno));
291
+		ignore_file(f);
292
+		return -1;
293
+	}
294
+
295
+	/* Cannot seek on these */
296
+	if (IS_PIPELIKE(finfo.st_mode) || f->fd == STDIN_FILENO)
297
+		return tail_pipe(f);
298
+
299
+	f->size = finfo.st_size;
300
+	f->blksize = finfo.st_blksize;	/* TODO: Can this value be 0? */
301
+
302
+	if (mode == M_LINES)
303
+		offset = lines_to_offset(f, n_units);
304
+	else
305
+		offset = bytes_to_offset(f, n_units);
306
+
307
+	/* We only get negative offsets on errors */
308
+	if (unlikely(offset < 0)) {
309
+		ignore_file(f);
310
+		return -1;
311
+	}
312
+
313
+	if (verbose)
314
+		write_header(f->name);
315
+
316
+	if (lseek(f->fd, offset, SEEK_SET) == (off_t) -1) {
317
+		fprintf(stderr, "Error: Could not seek in file '%s' (%s)\n", f->name, strerror(errno));
318
+		return -1;
319
+	}
320
+
321
+	buf = emalloc(f->blksize);
322
+
323
+	while ((bytes_read = read(f->fd, buf, f->blksize)) > 0)
324
+		write(STDOUT_FILENO, buf, (size_t) bytes_read);
325
+
326
+	if (!forever) {
327
+		if (close(f->fd) < 0) {
328
+			fprintf(stderr, "Error: Could not close file '%s' (%s)\n", f->name, strerror(errno));
329
+			free(buf);
330
+			return -1;
331
+		}
332
+	} /* Let the fd open otherwise, we'll need it */
333
+
334
+	free(buf);
335
+	return 0;
336
+}
337
+
338
+static int handle_inotify_event(struct inotify_event *inev, struct file_struct *f)
339
+{
340
+	int ret = 0;
341
+
342
+	if (inev->mask & IN_MODIFY) {
343
+		char *fbuf;
344
+		ssize_t rc;
345
+		struct stat finfo;
346
+
347
+		if (verbose)
348
+			write_header(f->name);
349
+
350
+		/* Seek to old file size */
351
+		if (lseek(f->fd, f->size, SEEK_SET) == (off_t) -1) {
352
+			fprintf(stderr, "Error: Could not seek in file '%s' (%s)\n", f->name, strerror(errno));
353
+			ret = -1;
354
+			goto ignore;
355
+		}
356
+
357
+		fbuf = emalloc(f->blksize);
358
+
359
+		while ((rc = read(f->fd, fbuf, f->blksize)) != 0)
360
+			write(STDOUT_FILENO, fbuf, (size_t) rc);
361
+
362
+		if (fstat(f->fd, &finfo) < 0) {
363
+			fprintf(stderr, "Error: Could not stat file '%s' (%s)\n", f->name, strerror(errno));
364
+			ret = -1;
365
+			free(fbuf);
366
+			goto ignore;
367
+		}
368
+
369
+		f->size = finfo.st_size;
370
+
371
+		free(fbuf);
372
+		return ret;
373
+	} else if (inev->mask & IN_DELETE_SELF) {
374
+		fprintf(stderr, "File '%s' deleted.\n", f->name);
375
+	} else if (inev->mask & IN_MOVE_SELF) {
376
+		fprintf(stderr, "File '%s' moved.\n", f->name);
377
+		return 0;
378
+	} else if (inev->mask & IN_UNMOUNT) {
379
+		fprintf(stderr, "Device containing file '%s' unmounted.\n", f->name);
380
+	}
381
+
382
+ignore:
383
+	ignore_file(f);
384
+	return ret;
385
+}
386
+
387
+static int watch_files(struct file_struct *files, int n_files)
388
+{
389
+	int ifd, i;
390
+	char buf[n_files * INOTIFY_BUFLEN];
391
+
392
+	ifd = inotify_init();
393
+	if (errno == ENOSYS) {
394
+		fprintf(stderr, "Error: inotify is not supported by the kernel you're currently running.\n");
395
+		exit(EXIT_FAILURE);
396
+	} else if (unlikely(ifd < 0)) {
397
+		fprintf(stderr, "Error: Could not initialize inotify (%s)\n", strerror(errno));
398
+		exit(EXIT_FAILURE);
399
+	}
400
+
401
+	for (i = 0; i < n_files; i++) {
402
+		if (!files[i].ignore) {
403
+			files[i].i_watch = inotify_add_watch(ifd, files[i].name,
404
+						IN_MODIFY|IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT);
405
+
406
+			if (files[i].i_watch < 0) {
407
+				fprintf(stderr, "Error: Could not create inotify watch on file '%s' (%s)\n",
408
+						files[i].name, strerror(errno));
409
+				ignore_file(&files[i]);
410
+			}
411
+		}
412
+	}
413
+
414
+	while (n_ignored < n_files) {
415
+		ssize_t len;
416
+		int ev_idx = 0;
417
+
418
+		len = read(ifd, buf, (n_files * INOTIFY_BUFLEN));
419
+		if (unlikely(len < 0)) {
420
+			/* Some signal, likely ^Z/fg's STOP and CONT interrupted the inotify read, retry */
421
+			if (errno == EINTR || errno == EAGAIN)
422
+				continue;
423
+			else {
424
+				fprintf(stderr, "Error: Could not read inotify events (%s)\n", strerror(errno));
425
+				exit(EXIT_FAILURE);
426
+			}
427
+		}
428
+
429
+		while (ev_idx < len) {
430
+			struct inotify_event *inev;
431
+			struct file_struct *f = NULL;
432
+
433
+			inev = (struct inotify_event *) &buf[ev_idx];
434
+
435
+			/* Which file has produced the event? */
436
+			for (i = 0; i < n_files; i++) {
437
+				if (!files[i].ignore
438
+						&& files[i].fd >= 0
439
+						&& files[i].i_watch == inev->wd) {
440
+					f = &files[i];
441
+					break;
442
+				}
443
+			}
444
+
445
+			if (unlikely(!f))
446
+				break;
447
+
448
+			if (handle_inotify_event(inev, f) < 0)
449
+				break;
450
+
451
+			ev_idx += sizeof(struct inotify_event) + inev->len;
452
+		}
453
+	}
454
+
455
+	close(ifd);
456
+	return -1;
457
+}
458
+
459
+int main(int argc, char **argv)
460
+{
461
+	int i, c, ret = 0;
462
+	int n_files;
463
+	unsigned long n_units = DEFAULT_N_LINES;
464
+	char forever = 0, mode = M_LINES;
465
+	char **filenames;
466
+	struct file_struct *files = NULL;
467
+
468
+	while ((c = getopt_long(argc, argv, "c:n:fvVh", long_opts, NULL)) != -1) {
469
+		switch (c) {
470
+		case 'c':
471
+			mode = M_BYTES;
472
+			/* fall through */
473
+		case 'n':
474
+			if (*optarg == '+') {
475
+				from_begin = 1;
476
+				optarg++;
477
+			} else if (*optarg == '-')
478
+				optarg++;
479
+
480
+			if (!is_digit(*optarg)) {
481
+				fprintf(stderr, "Error: Invalid number of units: %s\n", optarg);
482
+				exit(EXIT_FAILURE);
483
+			}
484
+			n_units = strtoul(optarg, NULL, 0);
485
+			break;
486
+                case 'f':
487
+			forever = 1;
488
+			break;
489
+		case 'v':
490
+			verbose = 1;
491
+			break;
492
+		case 'V':
493
+			fprintf(stdout, "%s %s\n", PROGRAM_NAME, VERSION);
494
+			exit(EXIT_SUCCESS);
495
+		case 'h':
496
+			usage(EXIT_SUCCESS);
497
+			break;
498
+		default:
499
+			usage(EXIT_FAILURE);
500
+		}
501
+	}
502
+
503
+	/* Do we have some files to read from? */
504
+	if (optind < argc) {
505
+		n_files = argc - optind;
506
+		filenames = argv + optind;
507
+	} else {
508
+		/* It must be stdin then */
509
+		static char *dummy_stdin = "-";
510
+		n_files = 1;
511
+		filenames = &dummy_stdin;
512
+
513
+		/* POSIX says that -f is ignored if no file operand is
514
+		   specified and standard input is a pipe. */
515
+		if (forever) {
516
+			struct stat finfo;
517
+			int rc = fstat(STDIN_FILENO, &finfo);
518
+
519
+			if (unlikely(rc == -1)) {
520
+				fprintf(stderr, "Error: Could not stat stdin (%s)\n", strerror(errno));
521
+				exit(EXIT_FAILURE);
522
+			}
523
+
524
+			if (rc == 0 && IS_PIPELIKE(finfo.st_mode))
525
+				forever = 0;
526
+		}
527
+	}
528
+
529
+	files = emalloc(n_files * sizeof(struct file_struct));
530
+
531
+	for (i = 0; i < n_files; i++) {
532
+		files[i].name = filenames[i];
533
+		setup_file(&files[i]);
534
+		ret = tail_file(&files[i], n_units, mode, forever);
535
+		if (ret < 0)
536
+			ignore_file(&files[i]);
537
+	}
538
+
539
+	if (forever)
540
+		ret = watch_files(files, n_files);
541
+
542
+	free(files);
543
+
544
+	return ret;
545
+}

+ 49
- 0
src/inotail/inotail.h Voir le fichier

@@ -0,0 +1,49 @@
1
+/*
2
+ * Copyright (C) 2005-2007, Tobias Klauser <tklauser@distanz.ch>
3
+ *
4
+ * Licensed under the terms of the GNU General Public License; version 2 or later.
5
+ */
6
+
7
+#ifndef _INOTAIL_H
8
+#define _INOTAIL_H
9
+
10
+#include <sys/types.h>
11
+
12
+/* Number of items to tail. */
13
+#define DEFAULT_N_LINES 10
14
+
15
+/* tail modes */
16
+enum { M_LINES, M_BYTES };
17
+
18
+/* Every tailed file is represented as a file_struct */
19
+struct file_struct {
20
+	char *name;		/* Name of file (or '-' for stdin) */
21
+	int fd;			/* File descriptor (or -1 if file is not open */
22
+	off_t size;		/* File size */
23
+	blksize_t blksize;	/* Blocksize for filesystem I/O */
24
+	unsigned ignore;	/* Whether to ignore the file in further processing */
25
+	int i_watch;		/* Inotify watch associated with file_struct */
26
+};
27
+
28
+#define IS_PIPELIKE(mode) \
29
+	(S_ISFIFO(mode) || S_ISSOCK(mode))
30
+
31
+/* inotail works on these file types */
32
+#define IS_TAILABLE(mode) \
33
+	(S_ISREG(mode) || IS_PIPELIKE(mode) || S_ISCHR(mode))
34
+
35
+#define is_digit(c) ((c) >= '0' && (c) <= '9')
36
+
37
+#ifdef DEBUG
38
+# define dprintf(fmt, args...) fprintf(stderr, fmt, ##args)
39
+#else
40
+# define dprintf(fmt, args...)
41
+#endif /* DEBUG */
42
+
43
+#ifdef __GNUC__
44
+# define unlikely(x) __builtin_expect(!!(x), 0)
45
+#else
46
+# define unlikely(x) (x)
47
+#endif /* __GNUC__ */
48
+
49
+#endif /* _INOTAIL_H */

+ 102
- 0
src/inotail/inotify-syscalls.h Voir le fichier

@@ -0,0 +1,102 @@
1
+/*
2
+ * Inotify syscall numbers
3
+ * Taken from the Linux kernel source tree
4
+ *
5
+ * Licensed under the terms of the GNU General Public License Version 2.
6
+ *
7
+ * Copyright (c) 2006-2007 Tobias Klauser <tklauser@distanz.ch>
8
+ */
9
+
10
+#ifndef _LINUX_INOTIFY_SYSCALLS_H
11
+#define _LINUX_INOTIFY_SYSCALLS_H
12
+
13
+#include <sys/syscall.h>
14
+/* glibc already defines them for some architectures */
15
+#ifndef __NR_inotify_init
16
+#if defined(__i386__)
17
+# define __NR_inotify_init	291
18
+# define __NR_inotify_add_watch	292
19
+# define __NR_inotify_rm_watch	293
20
+#elif defined(__x86_64__)
21
+# define __NR_inotify_init	253
22
+# define __NR_inotify_add_watch	254
23
+# define __NR_inotify_rm_watch	255
24
+#elif defined(__powerpc__) || defined(__powerpc64__)
25
+# define __NR_inotify_init	275
26
+# define __NR_inotify_add_watch	276
27
+# define __NR_inotify_rm_watch	277
28
+#elif defined (__ia64__)
29
+# define __NR_inotify_init	1277
30
+# define __NR_inotify_add_watch	1278
31
+# define __NR_inotify_rm_watch	1279
32
+#elif defined (__s390__)
33
+# define __NR_inotify_init	284
34
+# define __NR_inotify_add_watch	285
35
+# define __NR_inotify_rm_watch	286
36
+#elif defined (__alpha__)
37
+# define __NR_inotify_init	444
38
+# define __NR_inotify_add_watch	445
39
+# define __NR_inotify_rm_watch	446
40
+#elif defined (__sparc__) || defined (__sparc64__)
41
+# define __NR_inotify_init	151
42
+# define __NR_inotify_add_watch	152
43
+# define __NR_inotify_rm_watch	156
44
+#elif defined (__arm__)
45
+# define __NR_inotify_init	(__NR_SYSCALL_BASE + 316)
46
+# define __NR_inotify_add_watch	(__NR_SYSCALL_BASE + 317)
47
+# define __NR_inotify_rm_watch	(__NR_SYSCALL_BASE + 318)
48
+#elif defined (__sh__)
49
+# define __NR_inotify_init	290
50
+# define __NR_inotify_add_watch	291
51
+# define __NR_inotify_rm_watch	292
52
+#elif defined (__m32r__)
53
+# define __NR_inotify_init      290
54
+# define __NR_inotify_add_watch	291
55
+# define __NR_inotify_rm_watch	292
56
+#elif defined (__hppa__)
57
+# define __NR_inotify_init	269
58
+# define __NR_inotify_add_watch	270
59
+# define __NR_inotify_rm_watch	271
60
+#elif defined (__mips__)
61
+# include <sgidefs.h>
62
+# if _MIPS_SIM == _MIPS_SIM_ABI32
63
+#  define __NR_Linux			4000
64
+#  define __NR_inotify_init		(__NR_Linux + 284)
65
+#  define __NR_inotify_add_watch	(__NR_Linux + 285)
66
+#  define __NR_inotify_rm_watch		(__NR_Linux + 286)
67
+# elif _MIPS_SIM == _MIPS_SIM_ABI64
68
+#  define __NR_Linux			5000
69
+#  define __NR_inotify_init		(__NR_Linux + 243)
70
+#  define __NR_inotify_add_watch	(__NR_Linux + 244)
71
+#  define __NR_inotify_rm_watch		(__NR_Linux + 245)
72
+# elif _MIPS_SIM == _MIPS_SIM_NABI32
73
+#  define __NR_Linux			6000
74
+#  define __NR_inotify_init		(__NR_Linux + 247)
75
+#  define __NR_inotify_add_watch	(__NR_Linux + 248)
76
+#  define __NR_inotify_rm_watch		(__NR_Linux + 249)
77
+# endif
78
+#elif defined (__m68k__)
79
+# define __NR_inotify_init	284
80
+# define __NR_inotify_add_watch	285
81
+# define __NR_inotify_rm_watch	286
82
+#else
83
+# error "inotify not supported on this architecture!"
84
+#endif
85
+#endif	/* __NR_inotify_init */
86
+
87
+static inline int inotify_init(void)
88
+{
89
+	return syscall(__NR_inotify_init);
90
+}
91
+
92
+static inline int inotify_add_watch(int fd, const char *name, __u32 mask)
93
+{
94
+	return syscall(__NR_inotify_add_watch, fd, name, mask);
95
+}
96
+
97
+static inline int inotify_rm_watch(int fd, __u32 wd)
98
+{
99
+	return syscall(__NR_inotify_rm_watch, fd, wd);
100
+}
101
+
102
+#endif /* _LINUX_INOTIFY_SYSCALLS_H */

+ 69
- 0
src/inotail/inotify.h Voir le fichier

@@ -0,0 +1,69 @@
1
+/*
2
+ * Inode based directory notification for Linux
3
+ * Taken from the Linux kernel source tree
4
+ *
5
+ * Licensed under the terms of the GNU General Public License Version 2.
6
+ *
7
+ * Copyright (C) 2005 John McCutchan
8
+ */
9
+
10
+#ifndef _LINUX_INOTIFY_H
11
+#define _LINUX_INOTIFY_H
12
+
13
+#include <linux/types.h>
14
+
15
+/*
16
+ * struct inotify_event - structure read from the inotify device for each event
17
+ *
18
+ * When you are watching a directory, you will receive the filename for events
19
+ * such as IN_CREATE, IN_DELETE, IN_OPEN, IN_CLOSE, ..., relative to the wd.
20
+ */
21
+struct inotify_event {
22
+	__s32		wd;		/* watch descriptor */
23
+	__u32		mask;		/* watch mask */
24
+	__u32		cookie;		/* cookie to synchronize two events */
25
+	__u32		len;		/* length (including nulls) of name */
26
+	char		name[0];	/* stub for possible name */
27
+};
28
+
29
+/* the following are legal, implemented events that user-space can watch for */
30
+#define IN_ACCESS		0x00000001	/* File was accessed */
31
+#define IN_MODIFY		0x00000002	/* File was modified */
32
+#define IN_ATTRIB		0x00000004	/* Metadata changed */
33
+#define IN_CLOSE_WRITE		0x00000008	/* Writtable file was closed */
34
+#define IN_CLOSE_NOWRITE	0x00000010	/* Unwrittable file closed */
35
+#define IN_OPEN			0x00000020	/* File was opened */
36
+#define IN_MOVED_FROM		0x00000040	/* File was moved from X */
37
+#define IN_MOVED_TO		0x00000080	/* File was moved to Y */
38
+#define IN_CREATE		0x00000100	/* Subfile was created */
39
+#define IN_DELETE		0x00000200	/* Subfile was deleted */
40
+#define IN_DELETE_SELF		0x00000400	/* Self was deleted */
41
+#define IN_MOVE_SELF		0x00000800	/* Self was moved */
42
+
43
+/* the following are legal events.  they are sent as needed to any watch */
44
+#define IN_UNMOUNT		0x00002000	/* Backing fs was unmounted */
45
+#define IN_Q_OVERFLOW		0x00004000	/* Event queued overflowed */
46
+#define IN_IGNORED		0x00008000	/* File was ignored */
47
+
48
+/* helper events */
49
+#define IN_CLOSE		(IN_CLOSE_WRITE | IN_CLOSE_NOWRITE) /* close */
50
+#define IN_MOVE			(IN_MOVED_FROM | IN_MOVED_TO) /* moves */
51
+
52
+/* special flags */
53
+#define IN_ONLYDIR		0x01000000	/* only watch the path if it is a directory */
54
+#define IN_DONT_FOLLOW		0x02000000	/* don't follow a sym link */
55
+#define IN_MASK_ADD		0x20000000	/* add to the mask of an already existing watch */
56
+#define IN_ISDIR		0x40000000	/* event occurred against dir */
57
+#define IN_ONESHOT		0x80000000	/* only send event once */
58
+
59
+/*
60
+ * All of the events - we build the list by hand so that we can add flags in
61
+ * the future and not break backward compatibility.  Apps will get only the
62
+ * events that they originally wanted.  Be sure to add new events here!
63
+ */
64
+#define IN_ALL_EVENTS	(IN_ACCESS | IN_MODIFY | IN_ATTRIB | IN_CLOSE_WRITE | \
65
+			 IN_CLOSE_NOWRITE | IN_OPEN | IN_MOVED_FROM | \
66
+			 IN_MOVED_TO | IN_DELETE | IN_CREATE | IN_DELETE_SELF | \
67
+			 IN_MOVE_SELF)
68
+
69
+#endif	/* _LINUX_INOTIFY_H */

+ 14
- 0
src/websockify/Makefile Voir le fichier

@@ -0,0 +1,14 @@
1
+TARGETS=websockify
2
+CFLAGS += -fPIC
3
+
4
+all: $(TARGETS)
5
+
6
+websockify: websockify.o websocket.o
7
+	$(CC) $(LDFLAGS) $^ -lssl -lcrypto -o $@
8
+
9
+websocket.o: websocket.c websocket.h
10
+websockify.o: websockify.c websocket.h
11
+
12
+clean:
13
+	rm -f websockify *.o
14
+

+ 914
- 0
src/websockify/websocket.c Voir le fichier

@@ -0,0 +1,914 @@
1
+/*
2
+ * WebSocket lib with support for "wss://" encryption.
3
+ * Copyright 2010 Joel Martin
4
+ * Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3)
5
+ *
6
+ * You can make a cert/key with openssl using:
7
+ * openssl req -new -x509 -days 365 -nodes -out self.pem -keyout self.pem
8
+ * as taken from http://docs.python.org/dev/library/ssl.html#certificates
9
+ */
10
+#include <unistd.h>
11
+#include <stdio.h>
12
+#include <stdlib.h>
13
+#include <errno.h>
14
+#include <string.h>
15
+#include <sys/types.h> 
16
+#include <sys/socket.h>
17
+#include <sys/stat.h>
18
+#include <netinet/in.h>
19
+#include <arpa/inet.h>
20
+#include <netdb.h>
21
+#include <signal.h> // daemonizing
22
+#include <fcntl.h>  // daemonizing
23
+#include <openssl/err.h>
24
+#include <openssl/ssl.h>
25
+#include <openssl/bio.h> /* base64 encode/decode */
26
+#include <openssl/md5.h> /* md5 hash */
27
+#include <openssl/sha.h> /* sha1 hash */
28
+#include "websocket.h"
29
+
30
+/*
31
+ * Global state
32
+ *
33
+ *   Warning: not thread safe
34
+ */
35
+int ssl_initialized = 0;
36
+int pipe_error = 0;
37
+settings_t settings;
38
+
39
+
40
+void traffic(const char * token) {
41
+    if ((settings.verbose) && (! settings.daemon)) {
42
+        fprintf(stdout, "%s", token);
43
+        fflush(stdout);
44
+    }
45
+}
46
+
47
+void error(char *msg)
48
+{
49
+    perror(msg);
50
+}
51
+
52
+void fatal(char *msg)
53
+{
54
+    perror(msg);
55
+    exit(1);
56
+}
57
+
58
+/* resolve host with also IP address parsing */ 
59
+int resolve_host(struct in_addr *sin_addr, const char *hostname) 
60
+{ 
61
+    if (!inet_aton(hostname, sin_addr)) { 
62
+        struct addrinfo *ai, *cur; 
63
+        struct addrinfo hints; 
64
+        memset(&hints, 0, sizeof(hints)); 
65
+        hints.ai_family = AF_INET; 
66
+        if (getaddrinfo(hostname, NULL, &hints, &ai)) 
67
+            return -1; 
68
+        for (cur = ai; cur; cur = cur->ai_next) { 
69
+            if (cur->ai_family == AF_INET) { 
70
+                *sin_addr = ((struct sockaddr_in *)cur->ai_addr)->sin_addr; 
71
+                freeaddrinfo(ai); 
72
+                return 0; 
73
+            } 
74
+        } 
75
+        freeaddrinfo(ai); 
76
+        return -1; 
77
+    } 
78
+    return 0; 
79
+} 
80
+
81
+
82
+/*
83
+ * SSL Wrapper Code
84
+ */
85
+
86
+ssize_t ws_recv(ws_ctx_t *ctx, void *buf, size_t len) {
87
+    if (ctx->ssl) {
88
+        //handler_msg("SSL recv\n");
89
+        return SSL_read(ctx->ssl, buf, len);
90
+    } else {
91
+        return recv(ctx->sockfd, buf, len, 0);
92
+    }
93
+}
94
+
95
+ssize_t ws_send(ws_ctx_t *ctx, const void *buf, size_t len) {
96
+    if (ctx->ssl) {
97
+        //handler_msg("SSL send\n");
98
+        return SSL_write(ctx->ssl, buf, len);
99
+    } else {
100
+        return send(ctx->sockfd, buf, len, 0);
101
+    }
102
+}
103
+
104
+ws_ctx_t *alloc_ws_ctx() {
105
+    ws_ctx_t *ctx;
106
+    if (! (ctx = malloc(sizeof(ws_ctx_t))) )
107
+        { fatal("malloc()"); }
108
+
109
+    if (! (ctx->cin_buf = malloc(BUFSIZE)) )
110
+        { fatal("malloc of cin_buf"); }
111
+    if (! (ctx->cout_buf = malloc(BUFSIZE)) )
112
+        { fatal("malloc of cout_buf"); }
113
+    if (! (ctx->tin_buf = malloc(BUFSIZE)) )
114
+        { fatal("malloc of tin_buf"); }
115
+    if (! (ctx->tout_buf = malloc(BUFSIZE)) )
116
+        { fatal("malloc of tout_buf"); }
117
+
118
+    ctx->headers = malloc(sizeof(headers_t));
119
+    ctx->ssl = NULL;
120
+    ctx->ssl_ctx = NULL;
121
+    return ctx;
122
+}
123
+
124
+void free_ws_ctx(ws_ctx_t *ctx) {
125
+    free(ctx->cin_buf);
126
+    free(ctx->cout_buf);
127
+    free(ctx->tin_buf);
128
+    free(ctx->tout_buf);
129
+    free(ctx);
130
+}
131
+
132
+ws_ctx_t *ws_socket(ws_ctx_t *ctx, int socket) {
133
+    ctx->sockfd = socket;
134
+    return ctx;
135
+}
136
+
137
+ws_ctx_t *ws_socket_ssl(ws_ctx_t *ctx, int socket, char * certfile, char * keyfile) {
138
+    int ret;
139
+    char msg[1024];
140
+    char * use_keyfile;
141
+    ws_socket(ctx, socket);
142
+
143
+    if (keyfile && (keyfile[0] != '\0')) {
144
+        // Separate key file
145
+        use_keyfile = keyfile;
146
+    } else {
147
+        // Combined key and cert file
148
+        use_keyfile = certfile;
149
+    }
150
+
151
+    // Initialize the library
152
+    if (! ssl_initialized) {
153
+        SSL_library_init();
154
+        OpenSSL_add_all_algorithms();
155
+        SSL_load_error_strings();
156
+        ssl_initialized = 1;
157
+
158
+    }
159
+
160
+    //ctx->ssl_ctx = SSL_CTX_new(TLSv1_server_method());
161
+    ctx->ssl_ctx = SSL_CTX_new(TLS_server_method());
162
+    if (ctx->ssl_ctx == NULL) {
163
+        ERR_print_errors_fp(stderr);
164
+        fatal("Failed to configure SSL context");
165
+    }
166
+
167
+    if (SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, use_keyfile,
168
+                                    SSL_FILETYPE_PEM) <= 0) {
169
+        sprintf(msg, "Unable to load private key file %s\n", use_keyfile);
170
+        fatal(msg);
171
+    }
172
+
173
+    if (SSL_CTX_use_certificate_chain_file(ctx->ssl_ctx, certfile) <= 0) {
174
+        sprintf(msg, "Unable to load certificate file %s\n", certfile);
175
+        fatal(msg);
176
+    }
177
+
178
+//    if (SSL_CTX_set_cipher_list(ctx->ssl_ctx, "DEFAULT") != 1) {
179
+//        sprintf(msg, "Unable to set cipher\n");
180
+//        fatal(msg);
181
+//    }
182
+
183
+    // Associate socket and ssl object
184
+    ctx->ssl = SSL_new(ctx->ssl_ctx);
185
+    SSL_set_fd(ctx->ssl, socket);
186
+
187
+    ret = SSL_accept(ctx->ssl);
188
+    if (ret < 0) {
189
+        ERR_print_errors_fp(stderr);
190
+        return NULL;
191
+    }
192
+
193
+    return ctx;
194
+}
195
+
196
+void ws_socket_free(ws_ctx_t *ctx) {
197
+    if (ctx->ssl) {
198
+        SSL_free(ctx->ssl);
199
+        ctx->ssl = NULL;
200
+    }
201
+    if (ctx->ssl_ctx) {
202
+        SSL_CTX_free(ctx->ssl_ctx);
203
+        ctx->ssl_ctx = NULL;
204
+    }
205
+    if (ctx->sockfd) {
206
+        shutdown(ctx->sockfd, SHUT_RDWR);
207
+        close(ctx->sockfd);
208
+        ctx->sockfd = 0;
209
+    }
210
+}
211
+
212
+int ws_b64_ntop(const unsigned char const * src, size_t srclen, char * dst, size_t dstlen) {
213
+    int len = 0;
214
+    int total_len = 0;
215
+
216
+    BIO *buff, *b64f;
217
+    BUF_MEM *ptr;
218
+
219
+    b64f = BIO_new(BIO_f_base64());
220
+    buff = BIO_new(BIO_s_mem());
221
+    buff = BIO_push(b64f, buff);
222
+
223
+    BIO_set_flags(buff, BIO_FLAGS_BASE64_NO_NL);
224
+    BIO_set_close(buff, BIO_CLOSE);
225
+    do {
226
+        len = BIO_write(buff, src + total_len, srclen - total_len);
227
+        if (len > 0)
228
+            total_len += len;
229
+    } while (len && BIO_should_retry(buff));
230
+
231
+    BIO_flush(buff);
232
+
233
+    BIO_get_mem_ptr(buff, &ptr);
234
+    len = ptr->length;
235
+
236
+    memcpy(dst, ptr->data, dstlen < len ? dstlen : len);
237
+    dst[dstlen < len ? dstlen : len] = '\0';
238
+
239
+    BIO_free_all(buff);
240
+
241
+    if (dstlen < len)
242
+        return -1;
243
+
244
+    return len;
245
+}
246
+
247
+int ws_b64_pton(const char const * src, unsigned char * dst, size_t dstlen) {
248
+    int len = 0;
249
+    int total_len = 0;
250
+    int pending = 0;
251
+
252
+    BIO *buff, *b64f;
253
+
254
+    b64f = BIO_new(BIO_f_base64());
255
+    buff = BIO_new_mem_buf(src, -1);
256
+    buff = BIO_push(b64f, buff);
257
+
258
+    BIO_set_flags(buff, BIO_FLAGS_BASE64_NO_NL);
259
+    BIO_set_close(buff, BIO_CLOSE);
260
+    do {
261
+        len = BIO_read(buff, dst + total_len, dstlen - total_len);
262
+        if (len > 0)
263
+            total_len += len;
264
+    } while (len && BIO_should_retry(buff));
265
+
266
+    dst[total_len] = '\0';
267
+
268
+    pending = BIO_ctrl_pending(buff);
269
+
270
+    BIO_free_all(buff);
271
+
272
+    if (pending)
273
+        return -1;
274
+
275
+    return len;
276
+}
277
+
278
+/* ------------------------------------------------------- */
279
+
280
+
281
+int encode_hixie(u_char const *src, size_t srclength,
282
+                 char *target, size_t targsize) {
283
+    int sz = 0, len = 0;
284
+    target[sz++] = '\x00';
285
+    len = ws_b64_ntop(src, srclength, target+sz, targsize-sz);
286
+    if (len < 0) {
287
+        return len;
288
+    }
289
+    sz += len;
290
+    target[sz++] = '\xff';
291
+    return sz;
292
+}
293
+
294
+int decode_hixie(char *src, size_t srclength,
295
+                 u_char *target, size_t targsize,
296
+                 unsigned int *opcode, unsigned int *left) {
297
+    char *start, *end, cntstr[4];
298
+    int i, len, framecount = 0, retlen = 0;
299
+    unsigned char chr;
300
+    if ((src[0] != '\x00') || (src[srclength-1] != '\xff')) {
301
+        handler_emsg("WebSocket framing error\n");
302
+        return -1;
303
+    }
304
+    *left = srclength;
305
+
306
+    if (srclength == 2 &&
307
+        (src[0] == '\xff') && 
308
+        (src[1] == '\x00')) {
309
+        // client sent orderly close frame
310
+        *opcode = 0x8; // Close frame
311
+        return 0;
312
+    }
313
+    *opcode = 0x1; // Text frame
314
+
315
+    start = src+1; // Skip '\x00' start
316
+    do {
317
+        /* We may have more than one frame */
318
+        end = (char *)memchr(start, '\xff', srclength);
319
+        *end = '\x00';
320
+        len = ws_b64_pton(start, target+retlen, targsize-retlen);
321
+        if (len < 0) {
322
+            return len;
323
+        }
324
+        retlen += len;
325
+        start = end + 2; // Skip '\xff' end and '\x00' start 
326
+        framecount++;
327
+    } while (end < (src+srclength-1));
328
+    if (framecount > 1) {
329
+        //snprintf(cntstr, 3, "%d", framecount);
330
+        snprintf(cntstr, 4, "%d",  framecount > 999 ? 999 : framecount);
331
+        traffic(cntstr);
332
+    }
333
+    *left = 0;
334
+    return retlen;
335
+}
336
+
337
+int encode_hybi(u_char const *src, size_t srclength,
338
+                char *target, size_t targsize, unsigned int opcode)
339
+{
340
+    unsigned long long payload_offset = 2;
341
+    int len = 0;
342
+
343
+    if (opcode != OPCODE_TEXT && opcode != OPCODE_BINARY) {
344
+        handler_emsg("Invalid opcode. Opcode must be 0x01 for text mode, or 0x02 for binary mode.\n");
345
+        return -1;
346
+    }
347
+
348
+    target[0] = (char)((opcode & 0x0F) | 0x80);
349
+
350
+    if ((int)srclength <= 0) {
351
+        return 0;
352
+    }
353
+
354
+    if (opcode & OPCODE_TEXT) {
355
+        len = ((srclength - 1) / 3) * 4 + 4;
356
+    } else {
357
+        len = srclength;
358
+    }
359
+
360
+    if (len <= 125) {
361
+        target[1] = (char) len;
362
+        payload_offset = 2;
363
+    } else if ((len > 125) && (len < 65536)) {
364
+        target[1] = (char) 126;
365
+        *(u_short*)&(target[2]) = htons(len);
366
+        payload_offset = 4;
367
+    } else {
368
+        handler_emsg("Sending frames larger than 65535 bytes not supported\n");
369
+        return -1;
370
+        //target[1] = (char) 127;
371
+        //*(u_long*)&(target[2]) = htonl(b64_sz);
372
+        //payload_offset = 10;
373
+    }
374
+
375
+    if (opcode & OPCODE_TEXT) {
376
+        len = ws_b64_ntop(src, srclength, target+payload_offset, targsize-payload_offset);
377
+    } else {
378
+        memcpy(target+payload_offset, src, srclength);
379
+        len = srclength;
380
+    }
381
+
382
+    if (len < 0) {
383
+        return len;
384
+    }
385
+
386
+    return len + payload_offset;
387
+}
388
+
389
+int decode_hybi(unsigned char *src, size_t srclength,
390
+                u_char *target, size_t targsize,
391
+                unsigned int *opcode, unsigned int *left)
392
+{
393
+    unsigned char *frame, *mask, *payload, save_char;
394
+    char cntstr[4];
395
+    int masked = 0;
396
+    int i = 0, len, framecount = 0;
397
+    size_t remaining;
398
+    unsigned int target_offset = 0, hdr_length = 0, payload_length = 0;
399
+    
400
+    *left = srclength;
401
+    frame = src;
402
+
403
+    //printf("Deocde new frame\n");
404
+    while (1) {
405
+        // Need at least two bytes of the header
406
+        // Find beginning of next frame. First time hdr_length, masked and
407
+        // payload_length are zero
408
+        frame += hdr_length + 4*masked + payload_length;
409
+        //printf("frame[0..3]: 0x%x 0x%x 0x%x 0x%x (tot: %d)\n",
410
+        //       (unsigned char) frame[0],
411
+        //       (unsigned char) frame[1],
412
+        //       (unsigned char) frame[2],
413
+        //       (unsigned char) frame[3], srclength);
414
+
415
+        if (frame > src + srclength) {
416
+            //printf("Truncated frame from client, need %d more bytes\n", frame - (src + srclength) );
417
+            break;
418
+        }
419
+        remaining = (src + srclength) - frame;
420
+        if (remaining < 2) {
421
+            //printf("Truncated frame header from client\n");
422
+            break;
423
+        }
424
+        framecount ++;
425
+
426
+        *opcode = frame[0] & 0x0f;
427
+        masked = (frame[1] & 0x80) >> 7;
428
+
429
+        if (*opcode == 0x8) {
430
+            // client sent orderly close frame
431
+            break;
432
+        }
433
+
434
+        payload_length = frame[1] & 0x7f;
435
+        if (payload_length < 126) {
436
+            hdr_length = 2;
437
+            //frame += 2 * sizeof(char);
438
+        } else if (payload_length == 126) {
439
+            payload_length = (frame[2] << 8) + frame[3];
440
+            hdr_length = 4;
441
+        } else {
442
+            handler_emsg("Receiving frames larger than 65535 bytes not supported\n");
443
+            return -1;
444
+        }
445
+        if ((hdr_length + 4*masked + payload_length) > remaining) {
446
+            continue;
447
+        }
448
+        //printf("    payload_length: %u, raw remaining: %u\n", payload_length, remaining);
449
+        payload = frame + hdr_length + 4*masked;
450
+
451
+        if (*opcode != OPCODE_TEXT && *opcode != OPCODE_BINARY) {
452
+            handler_msg("Ignoring non-data frame, opcode 0x%x\n", *opcode);
453
+            continue;
454
+        }
455
+
456
+        if (payload_length == 0) {
457
+            handler_msg("Ignoring empty frame\n");
458
+            continue;
459
+        }
460
+
461
+        if ((payload_length > 0) && (!masked)) {
462
+            handler_emsg("Received unmasked payload from client\n");
463
+            return -1;
464
+        }
465
+
466
+        // Terminate with a null for base64 decode
467
+        save_char = payload[payload_length];
468
+        payload[payload_length] = '\0';
469
+
470
+        // unmask the data
471
+        mask = payload - 4;
472
+        for (i = 0; i < payload_length; i++) {
473
+            payload[i] ^= mask[i%4];
474
+        }
475
+
476
+        if (*opcode & OPCODE_TEXT) {
477
+            // base64 decode the data
478
+            len = ws_b64_pton((const char*)payload, target+target_offset, targsize);
479
+        } else {
480
+            memcpy(target+target_offset, payload, payload_length);
481
+            len = payload_length;
482
+        }
483
+
484
+        // Restore the first character of the next frame
485
+        payload[payload_length] = save_char;
486
+        if (len < 0) {
487
+            handler_emsg("Base64 decode error code %d", len);
488
+            return len;
489
+        }
490
+        target_offset += len;
491
+
492
+        //printf("    len %d, raw %s\n", len, frame);
493
+    }
494
+
495
+    if (framecount > 1) {
496
+        //snprintf(cntstr, 3, "%d", framecount);
497
+        snprintf(cntstr, 4, "%d",  framecount > 999 ? 999 : framecount);
498
+        traffic(cntstr);
499
+    }
500
+    
501
+    *left = remaining;
502
+    return target_offset;
503
+}
504
+
505
+
506
+
507
+int parse_handshake(ws_ctx_t *ws_ctx, char *handshake) {
508
+    char *start, *end;
509
+    headers_t *headers = ws_ctx->headers;
510
+
511
+    headers->key1[0] = '\0';
512
+    headers->key2[0] = '\0';
513
+    headers->key3[0] = '\0';
514
+    
515
+    if ((strlen(handshake) < 92) || (bcmp(handshake, "GET ", 4) != 0)) {
516
+        return 0;
517
+    }
518
+    start = handshake+4;
519
+    end = strstr(start, " HTTP/1.1");
520
+    if (!end) { return 0; }
521
+    strncpy(headers->path, start, end-start);
522
+    headers->path[end-start] = '\0';
523
+
524
+    start = strstr(handshake, "\r\nHost: ");
525
+    if (!start) { return 0; }
526
+    start += 8;
527
+    end = strstr(start, "\r\n");
528
+    strncpy(headers->host, start, end-start);
529
+    headers->host[end-start] = '\0';
530
+
531
+    headers->origin[0] = '\0';
532
+    start = strstr(handshake, "\r\nOrigin: ");
533
+    if (start) {
534
+        start += 10;
535
+    } else {
536
+        start = strstr(handshake, "\r\nSec-WebSocket-Origin: ");
537
+        if (!start) { return 0; }
538
+        start += 24;
539
+    }
540
+    end = strstr(start, "\r\n");
541
+    strncpy(headers->origin, start, end-start);
542
+    headers->origin[end-start] = '\0';
543
+   
544
+    start = strstr(handshake, "\r\nSec-WebSocket-Version: ");
545
+    if (start) {
546
+        // HyBi/RFC 6455
547
+        start += 25;
548
+        end = strstr(start, "\r\n");
549
+        strncpy(headers->version, start, end-start);
550
+        headers->version[end-start] = '\0';
551
+        ws_ctx->hixie = 0;
552
+        ws_ctx->hybi = strtol(headers->version, NULL, 10);
553
+
554
+        start = strstr(handshake, "\r\nSec-WebSocket-Key: ");
555
+        if (!start) { return 0; }
556
+        start += 21;
557
+        end = strstr(start, "\r\n");
558
+        strncpy(headers->key1, start, end-start);
559
+        headers->key1[end-start] = '\0';
560
+   
561
+        start = strstr(handshake, "\r\nConnection: ");
562
+        if (!start) { return 0; }
563
+        start += 14;
564
+        end = strstr(start, "\r\n");
565
+        strncpy(headers->connection, start, end-start);
566
+        headers->connection[end-start] = '\0';
567
+   
568
+        start = strstr(handshake, "\r\nSec-WebSocket-Protocol: ");
569
+        if (!start) { return 0; }
570
+        start += 26;
571
+        end = strstr(start, "\r\n");
572
+        strncpy(headers->protocols, start, end-start);
573
+        headers->protocols[end-start] = '\0';
574
+    } else {
575
+        // Hixie 75 or 76
576
+        ws_ctx->hybi = 0;
577
+
578
+        start = strstr(handshake, "\r\n\r\n");
579
+        if (!start) { return 0; }
580
+        start += 4;
581
+        if (strlen(start) == 8) {
582
+            ws_ctx->hixie = 76;
583
+            strncpy(headers->key3, start, 8);
584
+            headers->key3[8] = '\0';
585
+
586
+            start = strstr(handshake, "\r\nSec-WebSocket-Key1: ");
587
+            if (!start) { return 0; }
588
+            start += 22;
589
+            end = strstr(start, "\r\n");
590
+            strncpy(headers->key1, start, end-start);
591
+            headers->key1[end-start] = '\0';
592
+        
593
+            start = strstr(handshake, "\r\nSec-WebSocket-Key2: ");
594
+            if (!start) { return 0; }
595
+            start += 22;
596
+            end = strstr(start, "\r\n");
597
+            strncpy(headers->key2, start, end-start);
598
+            headers->key2[end-start] = '\0';
599
+        } else {
600
+            ws_ctx->hixie = 75;
601
+        }
602
+
603
+    }
604
+
605
+    return 1;
606
+}
607
+
608
+int parse_hixie76_key(char * key) {
609
+    unsigned long i, spaces = 0, num = 0;
610
+    for (i=0; i < strlen(key); i++) {
611
+        if (key[i] == ' ') {
612
+            spaces += 1;
613
+        }
614
+        if ((key[i] >= 48) && (key[i] <= 57)) {
615
+            num = num * 10 + (key[i] - 48);
616
+        }
617
+    }
618
+    return num / spaces;
619
+}
620
+
621
+int gen_md5(headers_t *headers, char *target) {
622
+    unsigned long key1 = parse_hixie76_key(headers->key1);
623
+    unsigned long key2 = parse_hixie76_key(headers->key2);
624
+    char *key3 = headers->key3;
625
+
626
+    MD5_CTX c;
627
+    char in[HIXIE_MD5_DIGEST_LENGTH] = {
628
+        key1 >> 24, key1 >> 16, key1 >> 8, key1,
629
+        key2 >> 24, key2 >> 16, key2 >> 8, key2,
630
+        key3[0], key3[1], key3[2], key3[3],
631
+        key3[4], key3[5], key3[6], key3[7]
632
+    };
633
+
634
+    MD5_Init(&c);
635
+    MD5_Update(&c, (void *)in, sizeof in);
636
+    MD5_Final((void *)target, &c);
637
+
638
+    target[HIXIE_MD5_DIGEST_LENGTH] = '\0';
639
+
640
+    return 1;
641
+}
642
+
643
+static void gen_sha1(headers_t *headers, char *target) {
644
+    SHA_CTX c;
645
+    unsigned char hash[SHA_DIGEST_LENGTH];
646
+    int r;
647
+
648
+    SHA1_Init(&c);
649
+    SHA1_Update(&c, headers->key1, strlen(headers->key1));
650
+    SHA1_Update(&c, HYBI_GUID, 36);
651
+    SHA1_Final(hash, &c);
652
+
653
+    r = ws_b64_ntop(hash, sizeof hash, target, HYBI10_ACCEPTHDRLEN);
654
+    //assert(r == HYBI10_ACCEPTHDRLEN - 1);
655
+}
656
+
657
+
658
+ws_ctx_t *do_handshake(int sock) {
659
+    char handshake[4096], response[4096], sha1[29], trailer[17];
660
+    char *scheme, *pre;
661
+    headers_t *headers;
662
+    int len, ret, i, offset;
663
+    ws_ctx_t * ws_ctx;
664
+    char *response_protocol;
665
+
666
+    // Peek, but don't read the data
667
+    len = recv(sock, handshake, 1024, MSG_PEEK);
668
+    handshake[len] = 0;
669
+    if (len == 0) {
670
+        handler_msg("ignoring empty handshake\n");
671
+        return NULL;
672
+    } else if ((bcmp(handshake, "\x16", 1) == 0) ||
673
+               (bcmp(handshake, "\x80", 1) == 0)) {
674
+        // SSL
675
+        if (!settings.cert) {
676
+            handler_msg("SSL connection but no cert specified\n");
677
+            return NULL;
678
+        } else if (access(settings.cert, R_OK) != 0) {
679
+            handler_msg("SSL connection but '%s' not found\n",
680
+                        settings.cert);
681
+            return NULL;
682
+        }
683
+        ws_ctx = alloc_ws_ctx();
684
+        ws_socket_ssl(ws_ctx, sock, settings.cert, settings.key);
685
+        if (! ws_ctx) { return NULL; }
686
+        scheme = "wss";
687
+        handler_msg("using SSL socket\n");
688
+    } else if (settings.ssl_only) {
689
+        handler_msg("non-SSL connection disallowed\n");
690
+        return NULL;
691
+    } else {
692
+        ws_ctx = alloc_ws_ctx();
693
+        ws_socket(ws_ctx, sock);
694
+        if (! ws_ctx) { return NULL; }
695
+        scheme = "ws";
696
+        handler_msg("using plain (not SSL) socket\n");
697
+    }
698
+    offset = 0;
699
+    for (i = 0; i < 10; i++) {
700
+        /* (offset + 1): reserve one byte for the trailing '\0' */
701
+        if (0 > (len = ws_recv(ws_ctx, handshake + offset, sizeof(handshake) - (offset + 1)))) {
702
+            handler_emsg("Read error during handshake: %m\n");
703
+            free_ws_ctx(ws_ctx);
704
+            return NULL;
705
+        } else if (0 == len) {
706
+            handler_emsg("Client closed during handshake\n");
707
+            free_ws_ctx(ws_ctx);
708
+            return NULL;
709
+        }
710
+        offset += len;
711
+        handshake[offset] = 0;
712
+        if (strstr(handshake, "\r\n\r\n")) {
713
+            break;
714
+        } else if (sizeof(handshake) <= (size_t)(offset + 1)) {
715
+            handler_emsg("Oversized handshake\n");
716
+            free_ws_ctx(ws_ctx);
717
+            return NULL;
718
+        } else if (9 == i) {
719
+            handler_emsg("Incomplete handshake\n");
720
+            free_ws_ctx(ws_ctx);
721
+            return NULL;
722
+        }
723
+        usleep(10);
724
+    }
725
+
726
+    //handler_msg("handshake: %s\n", handshake);
727
+    if (!parse_handshake(ws_ctx, handshake)) {
728
+        handler_emsg("Invalid WS request\n");
729
+        free_ws_ctx(ws_ctx);
730
+        return NULL;
731
+    }
732
+
733
+    headers = ws_ctx->headers;
734
+
735
+    response_protocol = strtok(headers->protocols, ",");
736
+    if (!response_protocol || !strlen(response_protocol)) {
737
+        ws_ctx->opcode = OPCODE_BINARY;
738
+        response_protocol = "null";
739
+    } else if (!strcmp(response_protocol, "base64")) {
740
+      ws_ctx->opcode = OPCODE_TEXT;
741
+    } else {
742
+        ws_ctx->opcode = OPCODE_BINARY;
743
+    }
744
+
745
+    if (ws_ctx->hybi > 0) {
746
+        handler_msg("using protocol HyBi/IETF 6455 %d\n", ws_ctx->hybi);
747
+        gen_sha1(headers, sha1);
748
+        snprintf(response, sizeof(response), SERVER_HANDSHAKE_HYBI, sha1, response_protocol);
749
+    } else {
750
+        if (ws_ctx->hixie == 76) {
751
+            handler_msg("using protocol Hixie 76\n");
752
+            gen_md5(headers, trailer);
753
+            pre = "Sec-";
754
+        } else {
755
+            handler_msg("using protocol Hixie 75\n");
756
+            trailer[0] = '\0';
757
+            pre = "";
758
+        }
759
+        snprintf(response, sizeof(response), SERVER_HANDSHAKE_HIXIE, pre, headers->origin,
760
+                 pre, scheme, headers->host, headers->path, pre, "base64", trailer);
761
+    }
762
+    
763
+    //handler_msg("response: %s\n", response);
764
+    ws_send(ws_ctx, response, strlen(response));
765
+
766
+    return ws_ctx;
767
+}
768
+
769
+void signal_handler(int sig) {
770
+    switch (sig) {
771
+        case SIGHUP: break; // ignore for now
772
+        case SIGPIPE: pipe_error = 1; break; // handle inline
773
+        case SIGTERM: exit(0); break;
774
+    }
775
+}
776
+
777
+void daemonize(int keepfd) {
778
+    int pid, i;
779
+
780
+    umask(0);
781
+    if (!chdir("/")) { handler_emsg("Unable to chdir(\"/\").\n"); }
782
+    if (!setgid(getgid())) { handler_emsg("Unable to setgid().\n"); }
783
+    if (!setuid(getuid())) { handler_emsg("Unable to setuid().\n"); }
784
+
785
+    /* Double fork to daemonize */
786
+    pid = fork();
787
+    if (pid<0) { fatal("fork error"); }
788
+    if (pid>0) { exit(0); }  // parent exits
789
+    setsid();                // Obtain new process group
790
+    pid = fork();
791
+    if (pid<0) { fatal("fork error"); }
792
+    if (pid>0) { exit(0); }  // parent exits
793
+
794
+    /* Signal handling */
795
+    signal(SIGHUP, signal_handler);   // catch HUP
796
+    signal(SIGTERM, signal_handler);  // catch kill
797
+
798
+    /* Close open files */
799
+    for (i=getdtablesize(); i>=0; --i) {
800
+        if (i != keepfd) {
801
+            close(i);
802
+        } else if (settings.verbose) {
803
+            printf("keeping fd %d\n", keepfd);
804
+        }
805
+    }
806
+    i=open("/dev/null", O_RDWR);  // Redirect stdin
807
+    if (!dup(i)) { handler_emsg("Unable redirect stdout.\n"); } // Redirect stdout
808
+    if (!dup(i)) { handler_emsg("Unable redirect stderr.\n"); } // Redirect stderr
809
+}
810
+
811
+
812
+void start_server() {
813
+    int lsock, csock, pid, sopt = 1, i;
814
+    struct sockaddr_in serv_addr, cli_addr;
815
+    socklen_t clilen;
816
+    ws_ctx_t *ws_ctx;
817
+
818
+
819
+    /* Initialize buffers */
820
+    lsock = socket(AF_INET, SOCK_STREAM, 0);
821
+    if (lsock < 0) { error("ERROR creating listener socket"); }
822
+    bzero((char *) &serv_addr, sizeof(serv_addr));
823
+    serv_addr.sin_family = AF_INET;
824
+    serv_addr.sin_port = htons(settings.listen_port);
825
+
826
+    /* Resolve listen address */
827
+    if (settings.listen_host && (settings.listen_host[0] != '\0')) {
828
+        if (resolve_host(&serv_addr.sin_addr, settings.listen_host) < -1) {
829
+            fatal("Could not resolve listen address");
830
+        }
831
+    } else {
832
+        serv_addr.sin_addr.s_addr = INADDR_ANY;
833
+    }
834
+
835
+    setsockopt(lsock, SOL_SOCKET, SO_REUSEADDR, (char *)&sopt, sizeof(sopt));
836
+    if (bind(lsock, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
837
+        fatal("ERROR on binding listener socket");
838
+    }
839
+    listen(lsock,100);
840
+
841
+    signal(SIGPIPE, signal_handler);  // catch pipe
842
+
843
+    if (settings.daemon) {
844
+        daemonize(lsock);
845
+    }
846
+
847
+
848
+    // Reep zombies
849
+    signal(SIGCHLD, SIG_IGN);
850
+
851
+    printf("Waiting for connections on %s:%d\n",
852
+            settings.listen_host, settings.listen_port);
853
+
854
+    while (1) {
855
+        clilen = sizeof(cli_addr);
856
+        pipe_error = 0;
857
+        pid = 0;
858
+        csock = accept(lsock, 
859
+                       (struct sockaddr *) &cli_addr, 
860
+                       &clilen);
861
+        if (csock < 0) {
862
+            error("ERROR on accept");
863
+            continue;
864
+        }
865
+        handler_msg("got client connection from %s\n",
866
+                    inet_ntoa(cli_addr.sin_addr));
867
+
868
+        if (!settings.run_once) {
869
+            handler_msg("forking handler process\n");
870
+            pid = fork();
871
+        }
872
+
873
+        if (pid == 0) {  // handler process
874
+            ws_ctx = do_handshake(csock);
875
+            if (settings.run_once) {
876
+                if (ws_ctx == NULL) {
877
+                    // Not a real WebSocket connection
878
+                    continue;
879
+                } else {
880
+                    // Successful connection, stop listening for new
881
+                    // connections
882
+                    close(lsock);
883
+                }
884
+            }
885
+            if (ws_ctx == NULL) {
886
+                handler_msg("No connection after handshake\n");
887
+                break;   // Child process exits
888
+            }
889
+
890
+            settings.handler(ws_ctx);
891
+            if (pipe_error) {
892
+                handler_emsg("Closing due to SIGPIPE\n");
893
+            }
894
+            break;   // Child process exits
895
+        } else {         // parent process
896
+            settings.handler_id += 1;
897
+            close(csock);
898
+        }
899
+    }
900
+    if (pid == 0) {
901
+        if (ws_ctx) {
902
+            ws_socket_free(ws_ctx);
903
+            free_ws_ctx(ws_ctx);
904
+        } else {
905
+            shutdown(csock, SHUT_RDWR);
906
+            close(csock);
907
+        }
908
+        handler_msg("handler exit\n");
909
+    } else {
910
+        handler_msg("websockify exit\n");
911
+    }
912
+
913
+}
914
+

+ 104
- 0
src/websockify/websocket.h Voir le fichier

@@ -0,0 +1,104 @@
1
+#include <openssl/ssl.h>
2
+
3
+#define BUFSIZE 65536
4
+#define DBUFSIZE (BUFSIZE * 3) / 4 - 20
5
+
6
+#define SERVER_HANDSHAKE_HIXIE "HTTP/1.1 101 Web Socket Protocol Handshake\r\n\
7
+Upgrade: WebSocket\r\n\
8
+Connection: Upgrade\r\n\
9
+%sWebSocket-Origin: %s\r\n\
10
+%sWebSocket-Location: %s://%s%s\r\n\
11
+%sWebSocket-Protocol: %s\r\n\
12
+\r\n%s"
13
+
14
+#define SERVER_HANDSHAKE_HYBI "HTTP/1.1 101 Switching Protocols\r\n\
15
+Upgrade: websocket\r\n\
16
+Connection: Upgrade\r\n\
17
+Sec-WebSocket-Accept: %s\r\n\
18
+Sec-WebSocket-Protocol: %s\r\n\
19
+\r\n"
20
+
21
+#define HYBI_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
22
+
23
+#define HYBI10_ACCEPTHDRLEN 29
24
+
25
+#define HIXIE_MD5_DIGEST_LENGTH 16
26
+
27
+#define POLICY_RESPONSE "<cross-domain-policy><allow-access-from domain=\"*\" to-ports=\"*\" /></cross-domain-policy>\n"
28
+
29
+#define OPCODE_TEXT    0x01
30
+#define OPCODE_BINARY  0x02
31
+
32
+typedef struct {
33
+    char path[1024+1];
34
+    char host[1024+1];
35
+    char origin[1024+1];
36
+    char version[1024+1];
37
+    char connection[1024+1];
38
+    char protocols[1024+1];
39
+    char key1[1024+1];
40
+    char key2[1024+1];
41
+    char key3[8+1];
42
+} headers_t;
43
+
44
+typedef struct {
45
+    int        sockfd;
46
+    SSL_CTX   *ssl_ctx;
47
+    SSL       *ssl;
48
+    int        hixie;
49
+    int        hybi;
50
+    int        opcode;
51
+    headers_t *headers;
52
+    char      *cin_buf;
53
+    char      *cout_buf;
54
+    char      *tin_buf;
55
+    char      *tout_buf;
56
+} ws_ctx_t;
57
+
58
+typedef struct {
59
+    int verbose;
60
+    char listen_host[256];
61
+    int listen_port;
62
+    void (*handler)(ws_ctx_t*);
63
+    int handler_id;
64
+    char *cert;
65
+    char *key;
66
+    int ssl_only;
67
+    int daemon;
68
+    int run_once;
69
+} settings_t;
70
+
71
+
72
+int resolve_host(struct in_addr *sin_addr, const char *hostname);
73
+
74
+ssize_t ws_recv(ws_ctx_t *ctx, void *buf, size_t len);
75
+
76
+ssize_t ws_send(ws_ctx_t *ctx, const void *buf, size_t len);
77
+
78
+/* base64.c declarations */
79
+//int b64_ntop(u_char const *src, size_t srclength, char *target, size_t targsize);
80
+//int b64_pton(char const *src, u_char *target, size_t targsize);
81
+
82
+#define gen_handler_msg(stream, ...) \
83
+    if (! settings.daemon) { \
84
+        fprintf(stream, "  %d: ", settings.handler_id); \
85
+        fprintf(stream, __VA_ARGS__); \
86
+    }
87
+
88
+#define handler_msg(...) gen_handler_msg(stdout, __VA_ARGS__);
89
+#define handler_emsg(...) gen_handler_msg(stderr, __VA_ARGS__);
90
+
91
+void traffic(const char * token);
92
+
93
+int encode_hixie(u_char const *src, size_t srclength,
94
+                 char *target, size_t targsize);
95
+int decode_hixie(char *src, size_t srclength,
96
+                 u_char *target, size_t targsize,
97
+                 unsigned int *opcode, unsigned int *left);
98
+int encode_hybi(u_char const *src, size_t srclength,
99
+                char *target, size_t targsize, unsigned int opcode);
100
+int decode_hybi(unsigned char *src, size_t srclength,
101
+                u_char *target, size_t targsize,
102
+                unsigned int *opcode, unsigned int *left);
103
+
104
+void start_server();

+ 388
- 0
src/websockify/websockify.c Voir le fichier

@@ -0,0 +1,388 @@
1
+/*
2
+ * A WebSocket to TCP socket proxy with support for "wss://" encryption.
3
+ * Copyright 2010 Joel Martin
4
+ * Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3)
5
+ *
6
+ * You can make a cert/key with openssl using:
7
+ * openssl req -new -x509 -days 365 -nodes -out self.pem -keyout self.pem
8
+ * as taken from http://docs.python.org/dev/library/ssl.html#certificates
9
+ */
10
+#include <stdio.h>
11
+#include <errno.h>
12
+#include <limits.h>
13
+#include <getopt.h>
14
+#include <string.h>
15
+#include <sys/socket.h>
16
+#include <netinet/in.h>
17
+#include <netdb.h>
18
+#include <sys/select.h>
19
+#include <fcntl.h>
20
+#include <unistd.h>
21
+#include <sys/stat.h>
22
+#include "websocket.h"
23
+
24
+char traffic_legend[] = "\n\
25
+Traffic Legend:\n\
26
+    }  - Client receive\n\
27
+    }. - Client receive partial\n\
28
+    {  - Target receive\n\
29
+\n\
30
+    >  - Target send\n\
31
+    >. - Target send partial\n\
32
+    <  - Client send\n\
33
+    <. - Client send partial\n\
34
+";
35
+
36
+char USAGE[] = "Usage: [options] " \
37
+               "[source_addr:]source_port target_addr:target_port\n\n" \
38
+               "  --verbose|-v       verbose messages and per frame traffic\n" \
39
+               "  --daemon|-D        become a daemon (background process)\n" \
40
+               "  --run-once         handle a single WebSocket connection and exit\n" \
41
+               "  --cert CERT        SSL certificate file\n" \
42
+               "  --key KEY          SSL key file (if separate from cert)\n" \
43
+               "  --ssl-only         disallow non-encrypted connections";
44
+
45
+#define usage(fmt, args...) \
46
+    fprintf(stderr, "%s\n\n", USAGE); \
47
+    fprintf(stderr, fmt , ## args); \
48
+    exit(1);
49
+
50
+char target_host[256];
51
+int target_port;
52
+
53
+extern int pipe_error;
54
+extern settings_t settings;
55
+
56
+void do_proxy(ws_ctx_t *ws_ctx, int target) {
57
+    fd_set rlist, wlist, elist;
58
+    struct timeval tv;
59
+    int i, maxfd, client = ws_ctx->sockfd;
60
+    unsigned int opcode, left, ret;
61
+    unsigned int tout_start, tout_end, cout_start, cout_end;
62
+    unsigned int tin_start, tin_end;
63
+    ssize_t len, bytes;
64
+
65
+    tout_start = tout_end = cout_start = cout_end;
66
+    tin_start = tin_end = 0;
67
+    maxfd = client > target ? client+1 : target+1;
68
+
69
+    while (1) {
70
+        tv.tv_sec = 1;
71
+        tv.tv_usec = 0;
72
+
73
+        FD_ZERO(&rlist);
74
+        FD_ZERO(&wlist);
75
+        FD_ZERO(&elist);
76
+
77
+        FD_SET(client, &elist);
78
+        FD_SET(target, &elist);
79
+
80
+        if (tout_end == tout_start) {
81
+            // Nothing queued for target, so read from client
82
+            FD_SET(client, &rlist);
83
+        } else {
84
+            // Data queued for target, so write to it
85
+            FD_SET(target, &wlist);
86
+        }
87
+        if (cout_end == cout_start) {
88
+            // Nothing queued for client, so read from target
89
+            FD_SET(target, &rlist);
90
+        } else {
91
+            // Data queued for client, so write to it
92
+            FD_SET(client, &wlist);
93
+        }
94
+
95
+        ret = select(maxfd, &rlist, &wlist, &elist, &tv);
96
+        if (pipe_error) { break; }
97
+
98
+        if (FD_ISSET(target, &elist)) {
99
+            handler_emsg("target exception\n");
100
+            break;
101
+        }
102
+        if (FD_ISSET(client, &elist)) {
103
+            handler_emsg("client exception\n");
104
+            break;
105
+        }
106
+
107
+        if (ret == -1) {
108
+            handler_emsg("select(): %s\n", strerror(errno));
109
+            break;
110
+        } else if (ret == 0) {
111
+            //handler_emsg("select timeout\n");
112
+            continue;
113
+        }
114
+
115
+        if (FD_ISSET(target, &wlist)) {
116
+            len = tout_end-tout_start;
117
+            bytes = send(target, ws_ctx->tout_buf + tout_start, len, 0);
118
+            if (pipe_error) { break; }
119
+            if (bytes < 0) {
120
+                handler_emsg("target connection error: %s\n",
121
+                             strerror(errno));
122
+                break;
123
+            }
124
+            tout_start += bytes;
125
+            if (tout_start >= tout_end) {
126
+                tout_start = tout_end = 0;
127
+                traffic(">");
128
+            } else {
129
+                traffic(">.");
130
+            }
131
+        }
132
+
133
+        if (FD_ISSET(client, &wlist)) {
134
+            len = cout_end-cout_start;
135
+            bytes = ws_send(ws_ctx, ws_ctx->cout_buf + cout_start, len);
136
+            if (pipe_error) { break; }
137
+            if (len < 3) {
138
+                handler_emsg("len: %d, bytes: %d: %d\n",
139
+                             (int) len, (int) bytes,
140
+                             (int) *(ws_ctx->cout_buf + cout_start));
141
+            }
142
+            cout_start += bytes;
143
+            if (cout_start >= cout_end) {
144
+                cout_start = cout_end = 0;
145
+                traffic("<");
146
+            } else {
147
+                traffic("<.");
148
+            }
149
+        }
150
+
151
+        if (FD_ISSET(target, &rlist)) {
152
+            bytes = recv(target, ws_ctx->cin_buf, DBUFSIZE , 0);
153
+            if (pipe_error) { break; }
154
+            if (bytes <= 0) {
155
+                handler_emsg("target closed connection\n");
156
+                break;
157
+            }
158
+            cout_start = 0;
159
+            if (ws_ctx->hybi) {
160
+                cout_end = encode_hybi(ws_ctx->cin_buf, bytes,
161
+                                   ws_ctx->cout_buf, BUFSIZE, ws_ctx->opcode);
162
+            } else {
163
+                cout_end = encode_hixie(ws_ctx->cin_buf, bytes,
164
+                                    ws_ctx->cout_buf, BUFSIZE);
165
+            }
166
+            /*
167
+            printf("encoded: ");
168
+            for (i=0; i< cout_end; i++) {
169
+                printf("%u,", (unsigned char) *(ws_ctx->cout_buf+i));
170
+            }
171
+            printf("\n");
172
+            */
173
+            if (cout_end < 0) {
174
+                handler_emsg("encoding error\n");
175
+                break;
176
+            }
177
+            traffic("{");
178
+        }
179
+
180
+        if (FD_ISSET(client, &rlist)) {
181
+            bytes = ws_recv(ws_ctx, ws_ctx->tin_buf + tin_end, BUFSIZE-1);
182
+            if (pipe_error) { break; }
183
+            if (bytes <= 0) {
184
+                handler_emsg("client closed connection\n");
185
+                break;
186
+            }
187
+            tin_end += bytes;
188
+            /*
189
+            printf("before decode: ");
190
+            for (i=0; i< bytes; i++) {
191
+                printf("%u,", (unsigned char) *(ws_ctx->tin_buf+i));
192
+            }
193
+            printf("\n");
194
+            */
195
+            if (ws_ctx->hybi) {
196
+                len = decode_hybi(ws_ctx->tin_buf + tin_start,
197
+                                  tin_end-tin_start,
198
+                                  ws_ctx->tout_buf, BUFSIZE-1,
199
+                                  &opcode, &left);
200
+            } else {
201
+                len = decode_hixie(ws_ctx->tin_buf + tin_start,
202
+                                   tin_end-tin_start,
203
+                                   ws_ctx->tout_buf, BUFSIZE-1,
204
+                                   &opcode, &left);
205
+            }
206
+
207
+            if (opcode == 8) {
208
+                handler_msg("client sent orderly close frame\n");
209
+                break;
210
+            }
211
+
212
+            /*
213
+            printf("decoded: ");
214
+            for (i=0; i< len; i++) {
215
+                printf("%u,", (unsigned char) *(ws_ctx->tout_buf+i));
216
+            }
217
+            printf("\n");
218
+            */
219
+            if (len < 0) {
220
+                handler_emsg("decoding error\n");
221
+                break;
222
+            }
223
+            if (left) {
224
+                tin_start = tin_end - left;
225
+                //printf("partial frame from client");
226
+            } else {
227
+                tin_start = 0;
228
+                tin_end = 0;
229
+            }
230
+
231
+            traffic("}");
232
+            tout_start = 0;
233
+            tout_end = len;
234
+        }
235
+    }
236
+}
237
+
238
+void proxy_handler(ws_ctx_t *ws_ctx) {
239
+    int tsock = 0;
240
+    struct sockaddr_in taddr;
241
+
242
+    handler_msg("connecting to: %s:%d\n", target_host, target_port);
243
+
244
+    tsock = socket(AF_INET, SOCK_STREAM, 0);
245
+    if (tsock < 0) {
246
+        handler_emsg("Could not create target socket: %s\n",
247
+                     strerror(errno));
248
+        return;
249
+    }
250
+    bzero((char *) &taddr, sizeof(taddr));
251
+    taddr.sin_family = AF_INET;
252
+    taddr.sin_port = htons(target_port);
253
+
254
+    /* Resolve target address */
255
+    if (resolve_host(&taddr.sin_addr, target_host) < -1) {
256
+        handler_emsg("Could not resolve target address: %s\n",
257
+                     strerror(errno));
258
+    }
259
+
260
+    if (connect(tsock, (struct sockaddr *) &taddr, sizeof(taddr)) < 0) {
261
+        handler_emsg("Could not connect to target: %s\n",
262
+                     strerror(errno));
263
+        close(tsock);
264
+        return;
265
+    }
266
+
267
+    if ((settings.verbose) && (! settings.daemon)) {
268
+        printf("%s", traffic_legend);
269
+    }
270
+
271
+    do_proxy(ws_ctx, tsock);
272
+
273
+    shutdown(tsock, SHUT_RDWR);
274
+    close(tsock);
275
+}
276
+
277
+int main(int argc, char *argv[])
278
+{
279
+    int fd, c, option_index = 0;
280
+    static int ssl_only = 0, daemon = 0, run_once = 0, verbose = 0;
281
+    char *found;
282
+    static struct option long_options[] = {
283
+        {"verbose",    no_argument,       &verbose,    'v'},
284
+        {"ssl-only",   no_argument,       &ssl_only,    1 },
285
+        {"daemon",     no_argument,       &daemon,     'D'},
286
+        /* ---- */
287
+        {"run-once",   no_argument,       0,           'r'},
288
+        {"cert",       required_argument, 0,           'c'},
289
+        {"key",        required_argument, 0,           'k'},
290
+        {0, 0, 0, 0}
291
+    };
292
+
293
+    settings.cert = realpath("self.pem", NULL);
294
+    if (!settings.cert) {
295
+        /* Make sure it's always set to something */
296
+        settings.cert = "self.pem";
297
+    }
298
+    settings.key = "";
299
+
300
+    while (1) {
301
+        c = getopt_long (argc, argv, "vDrc:k:",
302
+                         long_options, &option_index);
303
+
304
+        /* Detect the end */
305
+        if (c == -1) { break; }
306
+
307
+        switch (c) {
308
+            case 0:
309
+                break; // ignore
310
+            case 1:
311
+                break; // ignore
312
+            case 'v':
313
+                verbose = 1;
314
+                break;
315
+            case 'D':
316
+                daemon = 1;
317
+                break;
318
+            case 'r':
319
+                run_once = 1;
320
+                break;
321
+            case 'c':
322
+                settings.cert = realpath(optarg, NULL);
323
+                if (! settings.cert) {
324
+                    usage("No cert file at %s\n", optarg);
325
+                }
326
+                break;
327
+            case 'k':
328
+                settings.key = realpath(optarg, NULL);
329
+                if (! settings.key) {
330
+                    usage("No key file at %s\n", optarg);
331
+                }
332
+                break;
333
+            default:
334
+                usage("\n");
335
+        }
336
+    }
337
+    settings.verbose      = verbose;
338
+    settings.ssl_only     = ssl_only;
339
+    settings.daemon       = daemon;
340
+    settings.run_once     = run_once;
341
+
342
+    if ((argc-optind) != 2) {
343
+        usage("Invalid number of arguments\n");
344
+    }
345
+
346
+    found = strstr(argv[optind], ":");
347
+    if (found) {
348
+        memcpy(settings.listen_host, argv[optind], found-argv[optind]);
349
+        settings.listen_port = strtol(found+1, NULL, 10);
350
+    } else {
351
+        settings.listen_host[0] = '\0';
352
+        settings.listen_port = strtol(argv[optind], NULL, 10);
353
+    }
354
+    optind++;
355
+    if (settings.listen_port == 0) {
356
+        usage("Could not parse listen_port\n");
357
+    }
358
+
359
+    found = strstr(argv[optind], ":");
360
+    if (found) {
361
+        memcpy(target_host, argv[optind], found-argv[optind]);
362
+        target_port = strtol(found+1, NULL, 10);
363
+    } else {
364
+        usage("Target argument must be host:port\n");
365
+    }
366
+    if (target_port == 0) {
367
+        usage("Could not parse target port\n");
368
+    }
369
+
370
+    if (ssl_only) {
371
+        if (access(settings.cert, R_OK) != 0) {
372
+            usage("SSL only and cert file '%s' not found\n", settings.cert);
373
+        }
374
+    } else if (access(settings.cert, R_OK) != 0) {
375
+        fprintf(stderr, "Warning: '%s' not found\n", settings.cert);
376
+    }
377
+
378
+    //printf("  verbose: %d\n",   settings.verbose);
379
+    //printf("  ssl_only: %d\n",  settings.ssl_only);
380
+    //printf("  daemon: %d\n",    settings.daemon);
381
+    //printf("  run_once: %d\n",  settings.run_once);
382
+    //printf("  cert: %s\n",      settings.cert);
383
+    //printf("  key: %s\n",       settings.key);
384
+
385
+    settings.handler = proxy_handler; 
386
+    start_server();
387
+
388
+}

Chargement…
Annuler
Enregistrer