PageRenderTime 1114ms CodeModel.GetById 645ms app.highlight 240ms RepoModel.GetById 222ms app.codeStats 1ms

/client/sub_client.c

https://bitbucket.org/michal_harakal/mosquitto
C | 556 lines | 495 code | 30 blank | 31 comment | 132 complexity | 37804a72e7d6abc2739184106730c5f4 MD5 | raw file
  1/*
  2Copyright (c) 2009-2012 Roger Light <roger@atchoo.org>
  3All rights reserved.
  4
  5Redistribution and use in source and binary forms, with or without
  6modification, are permitted provided that the following conditions are met:
  7
  81. Redistributions of source code must retain the above copyright notice,
  9   this list of conditions and the following disclaimer.
 102. Redistributions in binary form must reproduce the above copyright
 11   notice, this list of conditions and the following disclaimer in the
 12   documentation and/or other materials provided with the distribution.
 133. Neither the name of mosquitto nor the names of its
 14   contributors may be used to endorse or promote products derived from
 15   this software without specific prior written permission.
 16
 17THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 18AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 19IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 20ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 21LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 22CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 23SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 24INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 25CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 26ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 27POSSIBILITY OF SUCH DAMAGE.
 28*/
 29
 30#include <assert.h>
 31#include <errno.h>
 32#include <stdio.h>
 33#include <stdlib.h>
 34#include <string.h>
 35#ifndef WIN32
 36#include <unistd.h>
 37#else
 38#include <process.h>
 39#include <winsock2.h>
 40#define snprintf sprintf_s
 41#endif
 42
 43#include <mosquitto.h>
 44
 45/* This struct is used to pass data to callbacks.
 46 * An instance "ud" is created in main() and populated, then passed to
 47 * mosquitto_new(). */
 48struct userdata {
 49	char **topics;
 50	int topic_count;
 51	int topic_qos;
 52	char *username;
 53	char *password;
 54	int verbose;
 55	bool quiet;
 56};
 57
 58void my_message_callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message)
 59{
 60	struct userdata *ud;
 61
 62	assert(obj);
 63	ud = (struct userdata *)obj;
 64
 65	if(ud->verbose){
 66		if(message->payloadlen){
 67			printf("%s %s\n", message->topic, (const char *)message->payload);
 68		}else{
 69			printf("%s (null)\n", message->topic);
 70		}
 71		fflush(stdout);
 72	}else{
 73		if(message->payloadlen){
 74			printf("%s\n", (const char *)message->payload);
 75			fflush(stdout);
 76		}
 77	}
 78}
 79
 80void my_connect_callback(struct mosquitto *mosq, void *obj, int result)
 81{
 82	int i;
 83	struct userdata *ud;
 84
 85	assert(obj);
 86	ud = (struct userdata *)obj;
 87
 88	if(!result){
 89		for(i=0; i<ud->topic_count; i++){
 90			mosquitto_subscribe(mosq, NULL, ud->topics[i], ud->topic_qos);
 91		}
 92	}else{
 93		if(result && !ud->quiet){
 94			fprintf(stderr, "%s\n", mosquitto_connack_string(result));
 95		}
 96	}
 97}
 98
 99void my_subscribe_callback(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos)
100{
101	int i;
102	struct userdata *ud;
103
104	assert(obj);
105	ud = (struct userdata *)obj;
106
107	if(!ud->quiet) printf("Subscribed (mid: %d): %d", mid, granted_qos[0]);
108	for(i=1; i<qos_count; i++){
109		if(!ud->quiet) printf(", %d", granted_qos[i]);
110	}
111	if(!ud->quiet) printf("\n");
112}
113
114void my_log_callback(struct mosquitto *mosq, void *obj, int level, const char *str)
115{
116	printf("%s\n", str);
117}
118
119void print_usage(void)
120{
121	printf("mosquitto_sub is a simple mqtt client that will subscribe to a single topic and print all messages it receives.\n\n");
122	printf("Usage: mosquitto_sub [-c] [-h host] [-k keepalive] [-p port] [-q qos] [-v] -t topic ...\n");
123	printf("                     [-i id] [-I id_prefix]\n");
124	printf("                     [-d] [--quiet]\n");
125	printf("                     [-u username [-P password]]\n");
126	printf("                     [--will-topic [--will-payload payload] [--will-qos qos] [--will-retain]]\n");
127#ifdef WITH_TLS
128	printf("                     [{--cafile file | --capath dir} [--cert file] [--key file]]\n");
129#ifdef WITH_TLS_PSK
130	printf("                     [--psk hex-key --psk-identity identity]\n");
131#endif
132#endif
133	printf("       mosquitto_sub --help\n\n");
134	printf(" -c : disable 'clean session' (store subscription and pending messages when client disconnects).\n");
135	printf(" -d : enable debug messages.\n");
136	printf(" -h : mqtt host to connect to. Defaults to localhost.\n");
137	printf(" -i : id to use for this client. Defaults to mosquitto_sub_ appended with the process id.\n");
138	printf(" -I : define the client id as id_prefix appended with the process id. Useful for when the\n");
139	printf("      broker is using the clientid_prefixes option.\n");
140	printf(" -k : keep alive in seconds for this client. Defaults to 60.\n");
141	printf(" -p : network port to connect to. Defaults to 1883.\n");
142	printf(" -q : quality of service level to use for the subscription. Defaults to 0.\n");
143	printf(" -t : mqtt topic to subscribe to. May be repeated multiple times.\n");
144	printf(" -u : provide a username (requires MQTT 3.1 broker)\n");
145	printf(" -v : print published messages verbosely.\n");
146	printf(" -P : provide a password (requires MQTT 3.1 broker)\n");
147	printf(" --help : display this message.\n");
148	printf(" --quiet : don't print error messages.\n");
149	printf(" --will-payload : payload for the client Will, which is sent by the broker in case of\n");
150	printf("                  unexpected disconnection. If not given and will-topic is set, a zero\n");
151	printf("                  length message will be sent.\n");
152	printf(" --will-qos : QoS level for the client Will.\n");
153	printf(" --will-retain : if given, make the client Will retained.\n");
154	printf(" --will-topic : the topic on which to publish the client Will.\n");
155#ifdef WITH_TLS
156	printf(" --cafile : path to a file containing trusted CA certificates to enable encrypted\n");
157	printf("            certificate based communication.\n");
158	printf(" --capath : path to a directory containing trusted CA certificates to enable encrypted\n");
159	printf("            communication.\n");
160	printf(" --cert : client certificate for authentication, if required by server.\n");
161	printf(" --key : client private key for authentication, if required by server.\n");
162#ifdef WITH_TLS_PSK
163	printf(" --psk : pre-shared-key in hexadecimal (no leading 0x) to enable TLS-PSK mode.\n");
164	printf(" --psk-identity : client identity string for TLS-PSK mode.\n");
165#endif
166#endif
167	printf("\nSee http://mosquitto.org/ for more information.\n\n");
168}
169
170int main(int argc, char *argv[])
171{
172	char *id = NULL;
173	char *id_prefix = NULL;
174	int i;
175	char *host = "localhost";
176	int port = 1883;
177	int keepalive = 60;
178	bool clean_session = true;
179	bool debug = false;
180	struct mosquitto *mosq = NULL;
181	int rc;
182	char hostname[256];
183	char err[1024];
184	struct userdata ud;
185	int len;
186	
187	char *will_payload = NULL;
188	long will_payloadlen = 0;
189	int will_qos = 0;
190	bool will_retain = false;
191	char *will_topic = NULL;
192
193	char *cafile = NULL;
194	char *capath = NULL;
195	char *certfile = NULL;
196	char *keyfile = NULL;
197
198	char *psk = NULL;
199	char *psk_identity = NULL;
200
201	memset(&ud, 0, sizeof(struct userdata));
202
203	for(i=1; i<argc; i++){
204		if(!strcmp(argv[i], "-p") || !strcmp(argv[i], "--port")){
205			if(i==argc-1){
206				fprintf(stderr, "Error: -p argument given but no port specified.\n\n");
207				print_usage();
208				return 1;
209			}else{
210				port = atoi(argv[i+1]);
211				if(port<1 || port>65535){
212					fprintf(stderr, "Error: Invalid port given: %d\n", port);
213					print_usage();
214					return 1;
215				}
216			}
217			i++;
218		}else if(!strcmp(argv[i], "-c") || !strcmp(argv[i], "--disable-clean-session")){
219			clean_session = false;
220		}else if(!strcmp(argv[i], "--cafile")){
221			if(i==argc-1){
222				fprintf(stderr, "Error: --cafile argument given but no file specified.\n\n");
223				print_usage();
224				return 1;
225			}else{
226				cafile = argv[i+1];
227			}
228			i++;
229		}else if(!strcmp(argv[i], "--capath")){
230			if(i==argc-1){
231				fprintf(stderr, "Error: --capath argument given but no directory specified.\n\n");
232				print_usage();
233				return 1;
234			}else{
235				capath = argv[i+1];
236			}
237			i++;
238		}else if(!strcmp(argv[i], "--cert")){
239			if(i==argc-1){
240				fprintf(stderr, "Error: --cert argument given but no file specified.\n\n");
241				print_usage();
242				return 1;
243			}else{
244				certfile = argv[i+1];
245			}
246			i++;
247		}else if(!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")){
248			debug = true;
249		}else if(!strcmp(argv[i], "--help")){
250			print_usage();
251			return 0;
252		}else if(!strcmp(argv[i], "-h") || !strcmp(argv[i], "--host")){
253			if(i==argc-1){
254				fprintf(stderr, "Error: -h argument given but no host specified.\n\n");
255				print_usage();
256				return 1;
257			}else{
258				host = argv[i+1];
259			}
260			i++;
261		}else if(!strcmp(argv[i], "-i") || !strcmp(argv[i], "--id")){
262			if(id_prefix){
263				fprintf(stderr, "Error: -i and -I argument cannot be used together.\n\n");
264				print_usage();
265				return 1;
266			}
267			if(i==argc-1){
268				fprintf(stderr, "Error: -i argument given but no id specified.\n\n");
269				print_usage();
270				return 1;
271			}else{
272				id = argv[i+1];
273			}
274			i++;
275		}else if(!strcmp(argv[i], "-I") || !strcmp(argv[i], "--id-prefix")){
276			if(id){
277				fprintf(stderr, "Error: -i and -I argument cannot be used together.\n\n");
278				print_usage();
279				return 1;
280			}
281			if(i==argc-1){
282				fprintf(stderr, "Error: -I argument given but no id prefix specified.\n\n");
283				print_usage();
284				return 1;
285			}else{
286				id_prefix = argv[i+1];
287			}
288			i++;
289		}else if(!strcmp(argv[i], "-k") || !strcmp(argv[i], "--keepalive")){
290			if(i==argc-1){
291				fprintf(stderr, "Error: -k argument given but no keepalive specified.\n\n");
292				print_usage();
293				return 1;
294			}else{
295				keepalive = atoi(argv[i+1]);
296				if(keepalive>65535){
297					fprintf(stderr, "Error: Invalid keepalive given: %d\n", keepalive);
298					print_usage();
299					return 1;
300				}
301			}
302			i++;
303		}else if(!strcmp(argv[i], "--key")){
304			if(i==argc-1){
305				fprintf(stderr, "Error: --key argument given but no file specified.\n\n");
306				print_usage();
307				return 1;
308			}else{
309				keyfile = argv[i+1];
310			}
311			i++;
312		}else if(!strcmp(argv[i], "--psk")){
313			if(i==argc-1){
314				fprintf(stderr, "Error: --psk argument given but no key specified.\n\n");
315				print_usage();
316				return 1;
317			}else{
318				psk = argv[i+1];
319			}
320			i++;
321		}else if(!strcmp(argv[i], "--psk-identity")){
322			if(i==argc-1){
323				fprintf(stderr, "Error: --psk-identity argument given but no identity specified.\n\n");
324				print_usage();
325				return 1;
326			}else{
327				psk_identity = argv[i+1];
328			}
329			i++;
330		}else if(!strcmp(argv[i], "-q") || !strcmp(argv[i], "--qos")){
331			if(i==argc-1){
332				fprintf(stderr, "Error: -q argument given but no QoS specified.\n\n");
333				print_usage();
334				return 1;
335			}else{
336				ud.topic_qos = atoi(argv[i+1]);
337				if(ud.topic_qos<0 || ud.topic_qos>2){
338					fprintf(stderr, "Error: Invalid QoS given: %d\n", ud.topic_qos);
339					print_usage();
340					return 1;
341				}
342			}
343			i++;
344		}else if(!strcmp(argv[i], "--quiet")){
345			ud.quiet = true;
346		}else if(!strcmp(argv[i], "-t") || !strcmp(argv[i], "--topic")){
347			if(i==argc-1){
348				fprintf(stderr, "Error: -t argument given but no topic specified.\n\n");
349				print_usage();
350				return 1;
351			}else{
352				ud.topic_count++;
353				ud.topics = realloc(ud.topics, ud.topic_count*sizeof(char *));
354				ud.topics[ud.topic_count-1] = argv[i+1];
355			}
356			i++;
357		}else if(!strcmp(argv[i], "-u") || !strcmp(argv[i], "--username")){
358			if(i==argc-1){
359				fprintf(stderr, "Error: -u argument given but no username specified.\n\n");
360				print_usage();
361				return 1;
362			}else{
363				ud.username = argv[i+1];
364			}
365			i++;
366		}else if(!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose")){
367			ud.verbose = 1;
368		}else if(!strcmp(argv[i], "-P") || !strcmp(argv[i], "--pw")){
369			if(i==argc-1){
370				fprintf(stderr, "Error: -P argument given but no password specified.\n\n");
371				print_usage();
372				return 1;
373			}else{
374				ud.password = argv[i+1];
375			}
376			i++;
377		}else if(!strcmp(argv[i], "--will-payload")){
378			if(i==argc-1){
379				fprintf(stderr, "Error: --will-payload argument given but no will payload specified.\n\n");
380				print_usage();
381				return 1;
382			}else{
383				will_payload = argv[i+1];
384				will_payloadlen = strlen(will_payload);
385			}
386			i++;
387		}else if(!strcmp(argv[i], "--will-qos")){
388			if(i==argc-1){
389				fprintf(stderr, "Error: --will-qos argument given but no will QoS specified.\n\n");
390				print_usage();
391				return 1;
392			}else{
393				will_qos = atoi(argv[i+1]);
394				if(will_qos < 0 || will_qos > 2){
395					fprintf(stderr, "Error: Invalid will QoS %d.\n\n", will_qos);
396					return 1;
397				}
398			}
399			i++;
400		}else if(!strcmp(argv[i], "--will-retain")){
401			will_retain = true;
402		}else if(!strcmp(argv[i], "--will-topic")){
403			if(i==argc-1){
404				fprintf(stderr, "Error: --will-topic argument given but no will topic specified.\n\n");
405				print_usage();
406				return 1;
407			}else{
408				will_topic = argv[i+1];
409			}
410			i++;
411		}else{
412			fprintf(stderr, "Error: Unknown option '%s'.\n",argv[i]);
413			print_usage();
414			return 1;
415		}
416	}
417
418	if(clean_session == false && (id_prefix || !id)){
419		if(!ud.quiet) fprintf(stderr, "Error: You must provide a client id if you are using the -c option.\n");
420		return 1;
421	}
422
423	if(ud.topic_count == 0){
424		fprintf(stderr, "Error: You must specify a topic to subscribe to.\n");
425		print_usage();
426		return 1;
427	}
428	if(will_payload && !will_topic){
429		fprintf(stderr, "Error: Will payload given, but no will topic given.\n");
430		print_usage();
431		return 1;
432	}
433	if(will_retain && !will_topic){
434		fprintf(stderr, "Error: Will retain given, but no will topic given.\n");
435		print_usage();
436		return 1;
437	}
438	if(ud.password && !ud.username){
439		if(!ud.quiet) fprintf(stderr, "Warning: Not using password since username not set.\n");
440	}
441	if((certfile && !keyfile) || (keyfile && !certfile)){
442		fprintf(stderr, "Error: Both certfile and keyfile must be provided if one of them is.\n");
443		print_usage();
444		return 1;
445	}
446	if((cafile || capath) && psk){
447		if(!ud.quiet) fprintf(stderr, "Error: Only one of --psk or --cafile/--capath may be used at once.\n");
448		return 1;
449	}
450	if(psk && !psk_identity){
451		if(!ud.quiet) fprintf(stderr, "Error: --psk-identity required if --psk used.\n");
452		return 1;
453	}
454
455	mosquitto_lib_init();
456
457	if(id_prefix){
458		id = malloc(strlen(id_prefix)+10);
459		if(!id){
460			if(!ud.quiet) fprintf(stderr, "Error: Out of memory.\n");
461			mosquitto_lib_cleanup();
462			return 1;
463		}
464		snprintf(id, strlen(id_prefix)+10, "%s%d", id_prefix, getpid());
465	}else if(!id){
466		hostname[0] = '\0';
467		gethostname(hostname, 256);
468		hostname[255] = '\0';
469		len = strlen("mosqsub/-") + 6 + strlen(hostname);
470		id = malloc(len);
471		if(!id){
472			if(!ud.quiet) fprintf(stderr, "Error: Out of memory.\n");
473			mosquitto_lib_cleanup();
474			return 1;
475		}
476		snprintf(id, len, "mosqsub/%d-%s", getpid(), hostname);
477		id[MOSQ_MQTT_ID_MAX_LENGTH] = '\0';
478	}
479
480	mosq = mosquitto_new(id, clean_session, &ud);
481	if(!mosq){
482		switch(errno){
483			case ENOMEM:
484				if(!ud.quiet) fprintf(stderr, "Error: Out of memory.\n");
485				break;
486			case EINVAL:
487				if(!ud.quiet) fprintf(stderr, "Error: Invalid id and/or clean_session.\n");
488				break;
489		}
490		mosquitto_lib_cleanup();
491		return 1;
492	}
493	if(debug){
494		mosquitto_log_callback_set(mosq, my_log_callback);
495	}
496	if(will_topic && mosquitto_will_set(mosq, will_topic, will_payloadlen, will_payload, will_qos, will_retain)){
497		if(!ud.quiet) fprintf(stderr, "Error: Problem setting will.\n");
498		mosquitto_lib_cleanup();
499		return 1;
500	}
501	if(ud.username && mosquitto_username_pw_set(mosq, ud.username, ud.password)){
502		if(!ud.quiet) fprintf(stderr, "Error: Problem setting username and password.\n");
503		mosquitto_lib_cleanup();
504		return 1;
505	}
506	if((cafile || capath) && mosquitto_tls_set(mosq, cafile, capath, certfile, keyfile, NULL)){
507		if(!ud.quiet) fprintf(stderr, "Error: Problem setting TLS options.\n");
508		mosquitto_lib_cleanup();
509		return 1;
510	}
511	if(psk && mosquitto_tls_psk_set(mosq, psk, psk_identity, NULL)){
512		if(!ud.quiet) fprintf(stderr, "Error: Problem setting TLS-PSK options.\n");
513		mosquitto_lib_cleanup();
514		return 1;
515	}
516	mosquitto_connect_callback_set(mosq, my_connect_callback);
517	mosquitto_message_callback_set(mosq, my_message_callback);
518	if(debug){
519		mosquitto_subscribe_callback_set(mosq, my_subscribe_callback);
520	}
521
522	rc = mosquitto_connect(mosq, host, port, keepalive);
523	if(rc){
524		if(!ud.quiet){
525			if(rc == MOSQ_ERR_ERRNO){
526#ifndef WIN32
527				strerror_r(errno, err, 1024);
528#else
529				FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, errno, 0, (LPTSTR)&err, 1024, NULL);
530#endif
531				fprintf(stderr, "Error: %s\n", err);
532			}else{
533				fprintf(stderr, "Unable to connect (%d).\n", rc);
534			}
535		}
536		return rc;
537		mosquitto_lib_cleanup();
538	}
539
540	do{
541		rc = mosquitto_loop(mosq, -1, 100);
542	}while(rc == MOSQ_ERR_SUCCESS);
543
544	mosquitto_destroy(mosq);
545	mosquitto_lib_cleanup();
546
547	if(rc){
548		if(rc == MOSQ_ERR_ERRNO){
549			fprintf(stderr, "Error: %s\n", strerror(errno));
550		}else{
551			fprintf(stderr, "Error: %s\n", mosquitto_strerror(rc));
552		}
553	}
554	return rc;
555}
556