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