1 diff -urN atftp-0.7.dfsg/atftpd.8 atftp-0.7.dfsg.patched/atftpd.8 2 --- atftp-0.7.dfsg/atftpd.8 2004-02-13 05:03:12.000000000 +0100 3 +++ atftp-0.7.dfsg.patched/atftpd.8 2009-04-16 19:30:15.000000000 +0200 4 @@ -161,6 +161,15 @@ 5 printout the substitution. 6 7 .TP 8 +.B \-\-content\-generator <path> 9 +Execute <path> to generate content for nonexisting files. If a 10 +requested file does not exist, the program will be called as <path> 11 +--file <requested file>. The generated contents must be written to 12 +file descriptor 3, which will be opened by atftpd using tmpfile(). 13 +If the contents need to be stored as well, the program itself is 14 +responsible for doing so, atftpd will not store the contents. 15 +
1 diff -urN atftp-0.7.dfsg/atftpd.8 atftp-0.7.dfsg.patched/atftpd.8
2 --- atftp-0.7.dfsg/atftpd.8 2004-02-13 05:03:12.000000000 +0100
3 +++ atftp-0.7.dfsg.patched/atftpd.8 2009-04-16 19:30:15.000000000 +0200
4 @@ -161,6 +161,15 @@
5 printout the substitution.
6
7 .TP
8 +.B \-\-content\-generator <path>
9 +Execute <path> to generate content for nonexisting files. If a
10 +requested file does not exist, the program will be called as <path>
11 +--file <requested file>. The generated contents must be written to
12 +file descriptor 3, which will be opened by atftpd using tmpfile().
13 +If the contents need to be stored as well, the program itself is
14 +responsible for doing so, atftpd will not store the contents.
15 +
16 +.TP
17 .B \-\-mtftp <file>
18 This will start a mtftp server thread for each valid entry in the
19 supplied file. See PXE specification for detail about mtftp. An
20 diff -urN atftp-0.7.dfsg/configure.ac atftp-0.7.dfsg.patched/configure.ac
21 --- atftp-0.7.dfsg/configure.ac 2004-03-16 02:51:40.000000000 +0100
22 +++ atftp-0.7.dfsg.patched/configure.ac 2009-04-13 22:36:36.000000000 +0200
23 @@ -45,6 +45,12 @@
24 yes) libpcre=true ;;
25 no) libpcre=flase ;;
26 esac], [libpcre=true])
27 +AC_ARG_ENABLE(generated_content,
28 + [ --enable-generated-content enable support for generated content in the server],
29 + [ case "${enableval}" in
30 + yes) generated_content=true ;;
31 + no) generated_content=false ;;
32 + esac], [generated_content=false])
33
34 AC_ARG_ENABLE(mtftp,
35 [ --enable-mtftp enable mtftp support in server],
36 @@ -170,6 +176,10 @@
37 "Support for libpcre pattern subsitution")])
38 AC_SUBST(LIBPCRE)
39 fi
40 +if test x$generated_content = xtrue; then
41 + AC_DEFINE([HAVE_GENERATED_CONTENT], 1,
42 + Support for generated content)
43 +fi
44
45 dnl Checks for header files.
46 AC_HEADER_STDC
47 diff -urN atftp-0.7.dfsg/debian/rules atftp-0.7.dfsg.patched/debian/rules
48 --- atftp-0.7.dfsg/debian/rules 2009-04-16 19:48:47.000000000 +0200
49 +++ atftp-0.7.dfsg.patched/debian/rules 2009-04-13 22:36:36.000000000 +0200
50 @@ -17,7 +17,7 @@
51 configure-stamp:
52 dh_testdir
53 # Add here commands to configure the package.
54 - ./configure --prefix=/usr --mandir=/usr/share/man
55 + ./configure --prefix=/usr --mandir=/usr/share/man --enable-generated-content
56
57 touch configure-stamp
58
59 diff -urN atftp-0.7.dfsg/README.GENERATED atftp-0.7.dfsg.patched/README.GENERATED
60 --- atftp-0.7.dfsg/README.GENERATED 1970-01-01 01:00:00.000000000 +0100
61 +++ atftp-0.7.dfsg.patched/README.GENERATED 2009-04-16 19:36:56.000000000 +0200
62 @@ -0,0 +1,61 @@
63 +Dynamically generating content with atftpd
64 +------------------------------------------
65 +
66 +When installing many machines via PXEboot kickstart/d-i installs, it is often
67 +useful to generate pxelinux.cfg files on demand with contents grabbed from a
68 +database. Or when you have an Asus Wl500g router, you have only tftp available
69 +to download data, being able to generate this data could be useful (eg. lists
70 +of macaddresses or iptables rules).
71 +
72 +These were my two use cases for implementing support for dynamic content in
73 +atftpd. Here is how to use it:
74 +
75 +* Add the "--content-generator /path/to/executable" argument to the atftpd
76 + commandline (eg. in xinetd.conf or /etc/default/atftpd, depending on your
77 + setup).
78 +* Whenever a requested file cannot be found, atftpd will open a temporary
79 + file with tmpfile(3) and will execute your application, with the
80 + requested name as arguments (example:
81 + /usr/local/bin/tftp-generator --file /var/lib/tftpboot/some_file)
82 + The application must ignore unknown arguments, more arguments might be added
83 + later.
84 +* Your application is now responsible for writing the content that atftpd
85 + should send to the given fd (6 in the example). If the content needs to
86 + be stored, your application also must do so itself.
87 +* If your application exists with a non-zero exitstatus or writes zero bytes,
88 + atftpd will treat that as "file not found". If data is written and your
89 + application exits with code 0, the written data is sent to the client.
90 +
91 +--
92 +Dennis Kaarsemaker
93 +<dennis@kaarsemaker.net>
94 +
95 +-------------------------------------------------------------------------------
96 +Example application for use case #2: Grabbing a list of mac addresses from a
97 +database. The database is maintained using the django framework.
98 +
99 +#!/usr/bin/python
100 +
101 +import os
102 +import sys
103 +from optparse import OptionParser
104 +
105 +parser = OptionParser()
106 +parser.add_option("--file", dest="filename", help="Requested file", metavar="FILE")
107 +options, args = parser.parse_args()
108 +
109 +if os.path.basename(options.filename) != 'maclist':
110 + sys.exit(1)
111 +fd = os.fdopen(3, 'w')
112 +
113 +sys.path.insert(0,'/srv/www/intranet')
114 +os.environ['DJANGO_SETTINGS_MODULE'] = 'infra.settings'
115 +from infra.machine.models import Interface
116 +
117 +fd.write("""#!/bin/sh
118 +# Shellscript generated from infra database
119 +# Do not edit by hand
120 +
121 +wl mac none
122 +wl mac %s 00:00:00:00:00:00
123 +""" % ' '.join([x.mac for x in Interface.objects.filter(wireless=True)]))
124 diff -urN atftp-0.7.dfsg/redhat/atftp.spec.in atftp-0.7.dfsg.patched/redhat/atftp.spec.in
125 --- atftp-0.7.dfsg/redhat/atftp.spec.in 2003-04-29 02:36:58.000000000 +0200
126 +++ atftp-0.7.dfsg.patched/redhat/atftp.spec.in 2009-04-16 19:50:41.000000000 +0200
127 @@ -33,7 +33,7 @@
128
129
130 %build
131 -%configure
132 +%configure --enable-generated-files
133 make
134
135
136 diff -urN atftp-0.7.dfsg/tftpd.c atftp-0.7.dfsg.patched/tftpd.c
137 --- atftp-0.7.dfsg/tftpd.c 2009-04-16 19:48:47.000000000 +0200
138 +++ atftp-0.7.dfsg.patched/tftpd.c 2009-04-13 22:36:36.000000000 +0200
139 @@ -112,6 +112,10 @@
140 char *pcre_file;
141 #endif
142
143 +#ifdef HAVE_GENERATED_CONTENT
144 +char *content_generator = NULL;
145 +#endif
146 +
147 #ifdef HAVE_MTFTP
148 /* mtftp options */
149 struct mtftp_data *mtftp_data = NULL;
150 @@ -839,6 +843,9 @@
151 { "pcre", 1, NULL, OPT_PCRE },
152 { "pcre-test", 1, NULL, OPT_PCRE_TEST },
153 #endif
154 +#ifdef HAVE_GENERATED_CONTENT
155 + { "content-generator", 1, NULL, 'g' },
156 +#endif
157 #ifdef HAVE_MTFTP
158 { "mtftp", 1, NULL, OPT_MTFTP },
159 { "mtftp-port", 1, NULL, OPT_MTFTP_PORT },
160 @@ -968,6 +975,15 @@
161 printf("Substitution: \"%s\" -> \"%s\"\n", string, out);
162 }
163 #endif
164 +#ifdef HAVE_GENERATED_CONTENT
165 + case 'g':
166 + content_generator = strdup(optarg);
167 + if(access(content_generator, X_OK)) {
168 + fprintf(stderr, "Cannot use %s as content generator: %s\n", content_generator, strerror(errno));
169 + content_generator = NULL;
170 + }
171 + break;
172 +#endif
173 case OPT_PORT_CHECK:
174 source_port_checking = 0;
175 break;
176 @@ -1053,6 +1069,10 @@
177 if (pcre_top)
178 logger(LOG_INFO, " PCRE: using file: %s", pcre_file);
179 #endif
180 +#ifdef HAVE_GENERATED_CONTENT
181 + if(content_generator)
182 + logger(LOG_INFO, " content generator: %s", content_generator);
183 +#endif
184 #ifdef HAVE_MTFTP
185 if (strcmp(mtftp_file, "") != 0)
186 {
187 @@ -1142,6 +1162,9 @@
188 " --pcre <file> : use this file for pattern replacement\n"
189 " --pcre-test <file> : just test pattern file, not starting server\n"
190 #endif
191 +#ifdef HAVE_GENERATED_CONTENT
192 + " --content-generator <path> : use <path> to generate content\n"
193 +#endif
194 #ifdef HAVE_MTFTP
195 " --mtftp <file> : mtftp configuration file\n"
196 " --mtftp-port <port> : port mtftp will listen\n"
197 diff -urN atftp-0.7.dfsg/tftpd_file.c atftp-0.7.dfsg.patched/tftpd_file.c
198 --- atftp-0.7.dfsg/tftpd_file.c 2004-02-18 03:21:47.000000000 +0100
199 +++ atftp-0.7.dfsg.patched/tftpd_file.c 2009-04-16 19:14:25.000000000 +0200
200 @@ -36,6 +36,10 @@
201 #ifdef HAVE_PCRE
202 #include "tftpd_pcre.h"
203 #endif
204 +#ifdef HAVE_GENERATED_CONTENT
205 +#include <sys/types.h>
206 +#include <sys/wait.h>
207 +#endif
208
209 #define S_BEGIN 0
210 #define S_SEND_REQ 1
211 @@ -60,6 +64,9 @@
212 extern tftpd_pcre_self_t *pcre_top;
213 #endif
214
215 +#ifdef HAVE_GENERATED_CONTENT
216 +extern char* content_generator;
217 +#endif
218
219 /*
220 * Rules for filenames. This is common to both tftpd_recieve_file
221 @@ -428,6 +435,10 @@
222 int prev_block_number = 0; /* needed to support netascii convertion */
223 int prev_file_pos = 0;
224 int temp = 0;
225 +#ifdef HAVE_GENERATED_CONTENT
226 + pid_t generator_pid = 0;
227 + int generator_status;
228 +#endif
229
230 /* look for mode option */
231 if (strcasecmp(data->tftp_options[OPT_MODE].value, "netascii") == 0)
232 @@ -485,6 +496,56 @@
233 }
234 }
235 #endif
236 +#ifdef HAVE_GENERATED_CONTENT
237 + if (fp == NULL)
238 + {
239 + if (content_generator)
240 + {
241 + logger(LOG_DEBUG, "Trying to generate contents for %s", filename);
242 + fp = tmpfile();
243 + generator_pid = fork();
244 + switch(generator_pid) {
245 + case 0:
246 + /* In the child */
247 + close(3);
248 + dup2(fileno(fp), 3);
249 + for(temp=4; temp < 1024; temp++)
250 + close(temp);
251 + execl(content_generator, content_generator, "--file", filename, (char *)NULL);
252 + /* Exec failed, make sure the parent notices */
253 + exit(66);
254 + ;;
255 + case -1:
256 + logger(LOG_WARNING, "fork() failed: %s", strerror(errno));
257 + fclose(fp);
258 + fp = NULL;
259 + default:
260 + /* In the parent */
261 + while(1) {
262 + errno = 0;
263 + temp = waitpid(generator_pid, &generator_status, 0);
264 + if(temp == -1 && errno == EINTR)
265 + continue;
266 + break;
267 + }
268 + if(!WIFEXITED(generator_status) || (WEXITSTATUS(generator_status) != 0)) {
269 + logger(LOG_WARNING, "generating content failed: %d %d", generator_status, WEXITSTATUS(generator_status));
270 + fclose(fp);
271 + fp = NULL;
272 + }
273 + else {
274 + fstat(fileno(fp), &file_stat);
275 + if(file_stat.st_size == 0) {
276 + logger(LOG_WARNING, "content generator wrote 0 bytes");
277 + fclose(fp);
278 + fp = NULL;
279 + }
280 + }
281 + break;
282 + }
283 + }
284 + }
285 +#endif
286 if (fp == NULL)
287 {
288 tftp_send_error(sockfd, sa, ENOTFOUND, data->data_buffer, data->data_buffer_size);
Show all