1 /*******************************************************************************
2 # #
3 # MJPG-streamer allows to stream JPG frames from an input-plugin #
4 # to several output plugins #
5 # #
6 # Copyright (C) 2007 Tom Stöveken #
7 # #
8 # This program is free software; you can redistribute it and/or modify #
9 # it under the terms of the GNU General Public License as published by #
10 # the Free Software Foundation; version 2 of the License. #
11 # #
12 # This program is distributed in the hope that it will be useful, #
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
15 # GNU General Public License for more details. #
16 # #
17 # You should have received a copy of the GNU General Public License #
18 # along with this program; if not, write to the Free Software #
19 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #
20 # #
21 *******************************************************************************/
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <string.h>
26 #include <sys/ioctl.h>
27 #include <errno.h>
28 #include <signal.h>
29 #include <sys/socket.h>
30 #include <arpa/inet.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <getopt.h>
34 #include <pthread.h>
35 #include <syslog.h>
36
37 #include <linux/types.h> /* for videodev2.h */
38 #include <linux/videodev2.h>
39
40 #include "../../mjpg_streamer.h"
41 #include "../../utils.h"
42 #include "httpd.h"
43
44 #define OUTPUT_PLUGIN_NAME "HTTP output plugin"
45 /*
46 * keep context for each server
47 */
48 context servers[MAX_OUTPUT_PLUGINS];
49
50 /******************************************************************************
51 Description.: print help for this plugin to stdout
52 Input Value.: -
53 Return Value: -
54 ******************************************************************************/
help(void)55 void help(void)
56 {
57 fprintf(stderr, " ---------------------------------------------------------------\n" \
58 " Help for output plugin..: "OUTPUT_PLUGIN_NAME"\n" \
59 " ---------------------------------------------------------------\n" \
60 " The following parameters can be passed to this plugin:\n\n" \
61 " [-w | --www ]...........: folder that contains webpages in \n" \
62 " flat hierarchy (no subfolders)\n" \
63 " [-p | --port ]..........: TCP port for this HTTP server\n" \
64 " [-l ] --listen ]........: Listen on Hostname / IP\n" \
65 " [-c | --credentials ]...: ask for \"username:password\" on connect\n" \
66 " [-n | --nocommands ]....: disable execution of commands\n"
67 " ---------------------------------------------------------------\n");
68 }
69
70 /*** plugin interface functions ***/
71 /******************************************************************************
72 Description.: Initialize this plugin.
73 parse configuration parameters,
74 store the parsed values in global variables
75 Input Value.: All parameters to work with.
76 Among many other variables the "param->id" is quite important -
77 it is used to distinguish between several server instances
78 Return Value: 0 if everything is OK, other values signal an error
79 ******************************************************************************/
output_init(output_parameter * param,int id)80 int output_init(output_parameter *param, int id)
81 {
82 int i;
83 int port;
84 char *credentials, *www_folder, *hostname = NULL;
85 char nocommands;
86
87 DBG("output #%02d\n", param->id);
88
89 port = htons(8080);
90 credentials = NULL;
91 www_folder = NULL;
92 nocommands = 0;
93
94 param->argv[0] = OUTPUT_PLUGIN_NAME;
95
96 /* show all parameters for DBG purposes */
97 for(i = 0; i < param->argc; i++) {
98 DBG("argv[%d]=%s\n", i, param->argv[i]);
99 }
100
101 reset_getopt();
102 while(1) {
103 int option_index = 0, c = 0;
104 static struct option long_options[] = {
105 {"h", no_argument, 0, 0
106 },
107 {"help", no_argument, 0, 0},
108 {"p", required_argument, 0, 0},
109 {"port", required_argument, 0, 0},
110 {"l", required_argument , 0, 0},
111 {"listen", required_argument, 0, 0},
112 {"c", required_argument, 0, 0},
113 {"credentials", required_argument, 0, 0},
114 {"w", required_argument, 0, 0},
115 {"www", required_argument, 0, 0},
116 {"n", no_argument, 0, 0},
117 {"nocommands", no_argument, 0, 0},
118 {0, 0, 0, 0}
119 };
120
121 c = getopt_long_only(param->argc, param->argv, "", long_options, &option_index);
122
123 /* no more options to parse */
124 if(c == -1) break;
125
126 /* unrecognized option */
127 if(c == '?') {
128 help();
129 return 1;
130 }
131
132 switch(option_index) {
133 /* h, help */
134 case 0:
135 case 1:
136 DBG("case 0,1\n");
137 help();
138 return 1;
139 break;
140
141 /* p, port */
142 case 2:
143 case 3:
144 DBG("case 2,3\n");
145 port = htons(atoi(optarg));
146 break;
147
148 /* Interface name */
149 case 4:
150 case 5:
151 DBG("case 4,5\n");
152 hostname = strdup(optarg);
153 break;
154
155 /* c, credentials */
156 case 6:
157 case 7:
158 DBG("case 6,7\n");
159 credentials = strdup(optarg);
160 break;
161
162 /* w, www */
163 case 8:
164 case 9:
165 DBG("case 8,9\n");
166 www_folder = malloc(strlen(optarg) + 2);
167 strcpy(www_folder, optarg);
168 if(optarg[strlen(optarg)-1] != '/')
169 strcat(www_folder, "/");
170 break;
171
172 /* n, nocommands */
173 case 10:
174 case 11:
175 DBG("case 10,11\n");
176 nocommands = 1;
177 break;
178 }
179 }
180
181 servers[param->id].id = param->id;
182 servers[param->id].pglobal = param->global;
183 servers[param->id].conf.port = port;
184 servers[param->id].conf.hostname = hostname;
185 servers[param->id].conf.credentials = credentials;
186 servers[param->id].conf.www_folder = www_folder;
187 servers[param->id].conf.nocommands = nocommands;
188
189 OPRINT("www-folder-path......: %s\n", (www_folder == NULL) ? "disabled" : www_folder);
190 OPRINT("HTTP TCP port........: %d\n", ntohs(port));
191 OPRINT("HTTP Listen Address..: %s\n", hostname);
192 OPRINT("username:password....: %s\n", (credentials == NULL) ? "disabled" : credentials);
193 OPRINT("commands.............: %s\n", (nocommands) ? "disabled" : "enabled");
194
195 param->global->out[id].name = malloc((strlen(OUTPUT_PLUGIN_NAME) + 1) * sizeof(char));
196 sprintf(param->global->out[id].name, OUTPUT_PLUGIN_NAME);
197
198 return 0;
199 }
200
201 /******************************************************************************
202 Description.: this will stop the server thread, client threads
203 will not get cleaned properly, because they run detached and
204 no pointer is kept. This is not a huge issue, because this
205 funtion is intended to clean up the biggest mess on shutdown.
206 Input Value.: id determines which server instance to send commands to
207 Return Value: always 0
208 ******************************************************************************/
output_stop(int id)209 int output_stop(int id)
210 {
211
212 DBG("will cancel server thread #%02d\n", id);
213 pthread_cancel(servers[id].threadID);
214
215 return 0;
216 }
217
218 /******************************************************************************
219 Description.: This creates and starts the server thread
220 Input Value.: id determines which server instance to send commands to
221 Return Value: always 0
222 ******************************************************************************/
output_run(int id)223 int output_run(int id)
224 {
225 DBG("launching server thread #%02d\n", id);
226
227 /* create thread and pass context to thread function */
228 pthread_create(&(servers[id].threadID), NULL, server_thread, &(servers[id]));
229 pthread_detach(servers[id].threadID);
230
231 return 0;
232 }
233
234 /******************************************************************************
235 Description.: This is just an example function, to show how the output
236 plugin could implement some special command.
237 If you want to control some GPIO Pin this is a good place to
238 implement it. Dont forget to add command types and a mapping.
239 Input Value.: cmd is the command type
240 id determines which server instance to send commands to
241 Return Value: 0 indicates success, other values indicate an error
242 ******************************************************************************/
output_cmd(int plugin,unsigned int control_id,unsigned int group,int value)243 int output_cmd(int plugin, unsigned int control_id, unsigned int group, int value)
244 {
245 DBG("command (%d, value: %d) for group %d triggered for plugin instance #%02d\n", control_id, value, group, plugin);
246 return 0;
247 }
248