1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <stdio.h>
18#include <errno.h>
19#include <stdbool.h>
20#include <string.h>
21#include <stdlib.h>
22#include <unistd.h>
23#include <getopt.h>
24
25#include <trusty/tipc.h>
26
27#define TIPC_DEFAULT_DEVNAME "/dev/trusty-ipc-dev0"
28
29static const char *dev_name = NULL;
30static const char *test_name = NULL;
31
32static const char *uuid_name = "com.android.ipc-unittest.srv.uuid";
33static const char *echo_name = "com.android.ipc-unittest.srv.echo";
34static const char *ta_only_name = "com.android.ipc-unittest.srv.ta_only";
35static const char *ns_only_name = "com.android.ipc-unittest.srv.ns_only";
36static const char *datasink_name = "com.android.ipc-unittest.srv.datasink";
37static const char *closer1_name = "com.android.ipc-unittest.srv.closer1";
38static const char *closer2_name = "com.android.ipc-unittest.srv.closer2";
39static const char *closer3_name = "com.android.ipc-unittest.srv.closer3";
40static const char *main_ctrl_name = "com.android.ipc-unittest.ctrl";
41
42static const char *_sopts = "hsvD:t:r:m:b:";
43static const struct option _lopts[] =  {
44	{"help",    no_argument,       0, 'h'},
45	{"silent",  no_argument,       0, 's'},
46	{"variable",no_argument,       0, 'v'},
47	{"dev",     required_argument, 0, 'D'},
48	{"repeat",  required_argument, 0, 'r'},
49	{"burst",   required_argument, 0, 'b'},
50	{"msgsize", required_argument, 0, 'm'},
51	{0, 0, 0, 0}
52};
53
54static const char *usage =
55"Usage: %s [options]\n"
56"\n"
57"options:\n"
58"  -h, --help            prints this message and exit\n"
59"  -D, --dev name        device name\n"
60"  -t, --test name       test to run\n"
61"  -r, --repeat cnt      repeat count\n"
62"  -m, --msgsize size    max message size\n"
63"  -v, --variable        variable message size\n"
64"  -s, --silent          silent\n"
65"\n"
66;
67
68static const char *usage_long =
69"\n"
70"The following tests are available:\n"
71"   connect      - connect to datasink service\n"
72"   connect_foo  - connect to non existing service\n"
73"   burst_write  - send messages to datasink service\n"
74"   echo         - send/receive messages to echo service\n"
75"   select       - test select call\n"
76"   blocked_read - test blocked read\n"
77"   closer1      - connection closed by remote (test1)\n"
78"   closer2      - connection closed by remote (test2)\n"
79"   closer3      - connection closed by remote (test3)\n"
80"   ta2ta-ipc    - execute TA to TA unittest\n"
81"   dev-uuid     - print device uuid\n"
82"   ta-access    - test ta-access flags\n"
83"\n"
84;
85
86static uint opt_repeat  = 1;
87static uint opt_msgsize = 32;
88static uint opt_msgburst = 32;
89static bool opt_variable = false;
90static bool opt_silent = false;
91
92static void print_usage_and_exit(const char *prog, int code, bool verbose)
93{
94	fprintf (stderr, usage, prog);
95	if (verbose)
96		fprintf (stderr, usage_long);
97	exit(code);
98}
99
100static void parse_options(int argc, char **argv)
101{
102	int c;
103	int oidx = 0;
104
105	while (1)
106	{
107		c = getopt_long (argc, argv, _sopts, _lopts, &oidx);
108		if (c == -1)
109			break; /* done */
110
111		switch (c) {
112
113		case 'D':
114			dev_name = strdup(optarg);
115		break;
116
117		case 't':
118			test_name = strdup(optarg);
119		break;
120
121		case 'v':
122			opt_variable = true;
123		break;
124
125		case 'r':
126			opt_repeat = atoi(optarg);
127		break;
128
129		case 'm':
130			opt_msgsize = atoi(optarg);
131		break;
132
133		case 'b':
134			opt_msgburst = atoi(optarg);
135		break;
136
137		case 's':
138			opt_silent = true;
139		break;
140
141		case 'h':
142		      print_usage_and_exit(argv[0], EXIT_SUCCESS, true);
143		break;
144
145		default:
146		      print_usage_and_exit(argv[0], EXIT_FAILURE, false);
147		}
148	}
149}
150
151static int connect_test(uint repeat)
152{
153	uint i;
154	int  echo_fd;
155	int  dsink_fd;
156
157	if (!opt_silent) {
158		printf("%s: repeat = %u\n", __func__, repeat);
159	}
160
161	for (i = 0; i < repeat; i++) {
162		echo_fd = tipc_connect(dev_name, echo_name);
163		if (echo_fd < 0) {
164			fprintf(stderr, "Failed to connect to '%s' service\n",
165				"echo");
166		}
167		dsink_fd = tipc_connect(dev_name, datasink_name);
168		if (dsink_fd < 0) {
169			fprintf(stderr, "Failed to connect to '%s' service\n",
170				"datasink");
171		}
172
173		if (echo_fd >= 0) {
174			tipc_close(echo_fd);
175		}
176		if (dsink_fd >= 0) {
177			tipc_close(dsink_fd);
178		}
179	}
180
181	if (!opt_silent) {
182		printf("%s: done\n", __func__);
183	}
184
185	return 0;
186}
187
188static int connect_foo(uint repeat)
189{
190	uint i;
191	int  fd;
192
193	if (!opt_silent) {
194		printf("%s: repeat = %u\n", __func__, repeat);
195	}
196
197	for (i = 0; i < repeat; i++) {
198		fd = tipc_connect(dev_name, "foo");
199		if (fd >= 0) {
200			fprintf(stderr, "succeeded to connect to '%s' service\n",
201				"foo");
202			tipc_close(fd);
203		}
204	}
205
206	if (!opt_silent) {
207		printf("%s: done\n", __func__);
208	}
209
210	return 0;
211}
212
213
214static int closer1_test(uint repeat)
215{
216	uint i;
217	int  fd;
218
219	if (!opt_silent) {
220		printf("%s: repeat = %u\n", __func__, repeat);
221	}
222
223	for (i = 0; i < repeat; i++) {
224		fd = tipc_connect(dev_name, closer1_name);
225		if (fd < 0) {
226			fprintf(stderr, "Failed to connect to '%s' service\n",
227				"closer1");
228			continue;
229		}
230		if (!opt_silent) {
231			printf("%s: connected\n", __func__);
232		}
233		tipc_close(fd);
234	}
235
236	if (!opt_silent) {
237		printf("%s: done\n", __func__);
238	}
239
240	return 0;
241}
242
243static int closer2_test(uint repeat)
244{
245	uint i;
246	int  fd;
247
248	if (!opt_silent) {
249		printf("%s: repeat = %u\n", __func__, repeat);
250	}
251
252	for (i = 0; i < repeat; i++) {
253		fd = tipc_connect(dev_name, closer2_name);
254		if (fd < 0) {
255			if (!opt_silent) {
256				printf("failed to connect to '%s' service\n", "closer2");
257			}
258		} else {
259			/* this should always fail */
260			fprintf(stderr, "connected to '%s' service\n", "closer2");
261			tipc_close(fd);
262		}
263	}
264
265	if (!opt_silent) {
266		printf("%s: done\n", __func__);
267	}
268
269	return 0;
270}
271
272static int closer3_test(uint repeat)
273{
274	uint i, j;
275	ssize_t rc;
276	int  fd[4];
277	char buf[64];
278
279	if (!opt_silent) {
280		printf("%s: repeat = %u\n", __func__, repeat);
281	}
282
283	for (i = 0; i < repeat; i++) {
284
285		/* open 4 connections to closer3 service */
286		for (j = 0; j < 4; j++) {
287			fd[j] = tipc_connect(dev_name, closer3_name);
288			if (fd[j] < 0) {
289				fprintf(stderr, "fd[%d]: failed to connect to '%s' service\n", j, "closer3");
290			} else {
291				if (!opt_silent) {
292					printf("%s: fd[%d]=%d: connected\n", __func__, j, fd[j]);
293				}
294				memset(buf, i + j, sizeof(buf));
295				rc = write(fd[j], buf, sizeof(buf));
296				if (rc != sizeof(buf)) {
297					if (!opt_silent) {
298						printf("%s: fd[%d]=%d: write returned  = %zd\n",
299							__func__, j, fd[j], rc);
300					}
301					perror("closer3_test: write");
302				}
303			}
304		}
305
306		/* sleep a bit */
307		sleep(1);
308
309		/* It is expected that they will be closed by remote */
310		for (j = 0; j < 4; j++) {
311			if (fd[j] < 0)
312				continue;
313			rc = write(fd[j], buf, sizeof(buf));
314			if (rc != sizeof(buf)) {
315				if (!opt_silent) {
316					printf("%s: fd[%d]=%d: write returned = %zd\n",
317						__func__, j, fd[j], rc);
318				}
319				perror("closer3_test: write");
320			}
321		}
322
323		/* then they have to be closed by remote */
324		for (j = 0; j < 4; j++) {
325			if (fd[j] >= 0) {
326				tipc_close(fd[j]);
327			}
328		}
329	}
330
331	if (!opt_silent) {
332		printf("%s: done\n", __func__);
333	}
334
335	return 0;
336}
337
338
339static int echo_test(uint repeat, uint msgsz, bool var)
340{
341	uint i;
342	ssize_t rc;
343	size_t  msg_len;
344	int  echo_fd =-1;
345	char tx_buf[msgsz];
346	char rx_buf[msgsz];
347
348	if (!opt_silent) {
349		printf("%s: repeat %u: msgsz %u: variable %s\n",
350			__func__, repeat, msgsz, var ? "true" : "false");
351	}
352
353	echo_fd = tipc_connect(dev_name, echo_name);
354	if (echo_fd < 0) {
355		fprintf(stderr, "Failed to connect to service\n");
356		return echo_fd;
357	}
358
359	for (i = 0; i < repeat; i++) {
360
361		msg_len = msgsz;
362		if (opt_variable && msgsz) {
363			msg_len = rand() % msgsz;
364		}
365
366		memset(tx_buf, i + 1, msg_len);
367
368		rc = write(echo_fd, tx_buf, msg_len);
369		if ((size_t)rc != msg_len) {
370			perror("echo_test: write");
371			break;
372		}
373
374		rc = read(echo_fd, rx_buf, msg_len);
375		if (rc < 0) {
376			perror("echo_test: read");
377			break;
378		}
379
380		if ((size_t)rc != msg_len) {
381			fprintf(stderr, "data truncated (%zu vs. %zu)\n",
382			                 rc, msg_len);
383			continue;
384		}
385
386		if (memcmp(tx_buf, rx_buf, (size_t) rc)) {
387			fprintf(stderr, "data mismatch\n");
388			continue;
389		}
390	}
391
392	tipc_close(echo_fd);
393
394	if (!opt_silent) {
395		printf("%s: done\n",__func__);
396	}
397
398	return 0;
399}
400
401static int burst_write_test(uint repeat, uint msgburst, uint msgsz, bool var)
402{
403	int fd;
404	uint i, j;
405	ssize_t rc;
406	size_t  msg_len;
407	char tx_buf[msgsz];
408
409	if (!opt_silent) {
410		printf("%s: repeat %u: burst %u: msgsz %u: variable %s\n",
411			__func__, repeat, msgburst, msgsz,
412			var ? "true" : "false");
413	}
414
415	for (i = 0; i < repeat; i++) {
416
417		fd = tipc_connect(dev_name, datasink_name);
418		if (fd < 0) {
419			fprintf(stderr, "Failed to connect to '%s' service\n",
420				"datasink");
421			break;
422		}
423
424		for (j = 0; j < msgburst; j++) {
425			msg_len = msgsz;
426			if (var && msgsz) {
427				msg_len = rand() % msgsz;
428			}
429
430			memset(tx_buf, i + 1, msg_len);
431			rc = write(fd, tx_buf, msg_len);
432			if ((size_t)rc != msg_len) {
433				perror("burst_test: write");
434				break;
435			}
436		}
437
438		tipc_close(fd);
439	}
440
441	if (!opt_silent) {
442		printf("%s: done\n",__func__);
443	}
444
445	return 0;
446}
447
448
449static int _wait_for_msg(int fd, uint msgsz, int timeout)
450{
451	int rc;
452	fd_set rfds;
453	uint msgcnt = 0;
454	char rx_buf[msgsz];
455	struct timeval tv;
456
457	if (!opt_silent) {
458		printf("waiting (%d) for msg\n", timeout);
459	}
460
461	FD_ZERO(&rfds);
462	FD_SET(fd, &rfds);
463
464	tv.tv_sec = timeout;
465	tv.tv_usec = 0;
466
467	for(;;) {
468		rc = select(fd+1, &rfds, NULL, NULL, &tv);
469
470		if (rc == 0) {
471			if (!opt_silent) {
472				printf("select timedout\n");
473			}
474			break;
475		}
476
477		if (rc == -1) {
478			perror("select_test: select");
479			return rc;
480		}
481
482		rc = read(fd, rx_buf, sizeof(rx_buf));
483		if (rc < 0) {
484			perror("select_test: read");
485			return rc;
486		} else {
487			if (rc > 0) {
488				msgcnt++;
489			}
490		}
491	}
492
493	if (!opt_silent) {
494		printf("got %u messages\n", msgcnt);
495	}
496
497	return 0;
498}
499
500
501static int select_test(uint repeat, uint msgburst, uint msgsz)
502{
503	int fd;
504	uint i, j;
505	ssize_t rc;
506	char tx_buf[msgsz];
507
508	if (!opt_silent) {
509		printf("%s: repeat %u\n", __func__, repeat);
510	}
511
512	fd = tipc_connect(dev_name, echo_name);
513	if (fd < 0) {
514		fprintf(stderr, "Failed to connect to '%s' service\n",
515			"echo");
516		return fd;
517	}
518
519	for (i = 0; i < repeat; i++) {
520
521		_wait_for_msg(fd, msgsz, 1);
522
523		if (!opt_silent) {
524			printf("sending burst: %u msg\n", msgburst);
525		}
526
527		for (j = 0; j < msgburst; j++) {
528			memset(tx_buf, i + j, msgsz);
529			rc = write(fd, tx_buf, msgsz);
530			if ((size_t)rc != msgsz) {
531				perror("burst_test: write");
532				break;
533			}
534		}
535	}
536
537	tipc_close(fd);
538
539	if (!opt_silent) {
540		printf("%s: done\n",__func__);
541	}
542
543	return 0;
544}
545
546static int blocked_read_test(uint repeat)
547{
548	int fd;
549	uint i;
550	ssize_t rc;
551	char rx_buf[512];
552
553	if (!opt_silent) {
554		printf("%s: repeat %u\n", __func__, repeat);
555	}
556
557	fd = tipc_connect(dev_name, echo_name);
558	if (fd < 0) {
559		fprintf(stderr, "Failed to connect to '%s' service\n",
560			"echo");
561		return fd;
562	}
563
564	for (i = 0; i < repeat; i++) {
565		rc = read(fd, rx_buf, sizeof(rx_buf));
566		if (rc < 0) {
567			perror("select_test: read");
568			break;
569		} else {
570			if (!opt_silent) {
571				printf("got %zd bytes\n", rc);
572			}
573		}
574	}
575
576	tipc_close(fd);
577
578	if (!opt_silent) {
579		printf("%s: done\n",__func__);
580	}
581
582	return 0;
583}
584
585static int ta2ta_ipc_test(void)
586{
587	int fd;
588	char rx_buf[64];
589
590	if (!opt_silent) {
591		printf("%s:\n", __func__);
592	}
593
594	fd = tipc_connect(dev_name, main_ctrl_name);
595	if (fd < 0) {
596		fprintf(stderr, "Failed to connect to '%s' service\n",
597			"main_ctrl");
598		return fd;
599	}
600
601	/* wait for test to complete */
602	(void) read(fd, rx_buf, sizeof(rx_buf));
603
604	tipc_close(fd);
605
606	return 0;
607}
608
609typedef struct uuid
610{
611	uint32_t time_low;
612	uint16_t time_mid;
613	uint16_t time_hi_and_version;
614	uint8_t clock_seq_and_node[8];
615} uuid_t;
616
617static void print_uuid(const char *dev, uuid_t *uuid)
618{
619	printf("%s:", dev);
620	printf("uuid: %08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
621	       uuid->time_low,
622	       uuid->time_mid,
623	       uuid->time_hi_and_version,
624	       uuid->clock_seq_and_node[0],
625	       uuid->clock_seq_and_node[1],
626	       uuid->clock_seq_and_node[2],
627	       uuid->clock_seq_and_node[3],
628	       uuid->clock_seq_and_node[4],
629	       uuid->clock_seq_and_node[5],
630	       uuid->clock_seq_and_node[6],
631	       uuid->clock_seq_and_node[7]
632	       );
633}
634
635static int dev_uuid_test(void)
636{
637	int fd;
638	ssize_t rc;
639	uuid_t uuid;
640
641	fd = tipc_connect(dev_name, uuid_name);
642	if (fd < 0) {
643		fprintf(stderr, "Failed to connect to '%s' service\n",
644			"uuid");
645		return fd;
646	}
647
648	/* wait for test to complete */
649	rc = read(fd, &uuid, sizeof(uuid));
650	if (rc < 0) {
651		perror("dev_uuid_test: read");
652	} else if (rc != sizeof(uuid)) {
653		fprintf(stderr, "unexpected uuid size (%d vs. %d)\n",
654			(int)rc, (int)sizeof(uuid));
655	} else {
656		print_uuid(dev_name, &uuid);
657	}
658
659	tipc_close(fd);
660
661	return 0;
662}
663
664static int ta_access_test(void)
665{
666	int fd;
667
668	if (!opt_silent) {
669		printf("%s:\n", __func__);
670	}
671
672	fd = tipc_connect(dev_name, ta_only_name);
673	if (fd >= 0) {
674		fprintf(stderr, "Succeed to connect to '%s' service\n",
675			"ta_only");
676		tipc_close(fd);
677	}
678
679	fd = tipc_connect(dev_name, ns_only_name);
680	if (fd < 0) {
681		fprintf(stderr, "Failed to connect to '%s' service\n",
682			"ns_only");
683		return fd;
684	}
685	tipc_close(fd);
686
687	if (!opt_silent) {
688		printf("%s: done\n",__func__);
689	}
690
691	return 0;
692}
693
694
695int main(int argc, char **argv)
696{
697	int rc = 0;
698
699	if (argc <= 1) {
700		print_usage_and_exit(argv[0], EXIT_FAILURE, false);
701	}
702
703	parse_options(argc, argv);
704
705	if (!dev_name) {
706		dev_name = TIPC_DEFAULT_DEVNAME;
707	}
708
709	if (!test_name) {
710		fprintf(stderr, "need a Test to run\n");
711		print_usage_and_exit(argv[0], EXIT_FAILURE, true);
712	}
713
714	if (strcmp(test_name, "connect") == 0) {
715		rc = connect_test(opt_repeat);
716	} else if (strcmp(test_name, "connect_foo") == 0) {
717		rc = connect_foo(opt_repeat);
718	} else if (strcmp(test_name, "burst_write") == 0) {
719		rc = burst_write_test(opt_repeat, opt_msgburst, opt_msgsize, opt_variable);
720	} else if (strcmp(test_name, "select") == 0) {
721		rc = select_test(opt_repeat, opt_msgburst,  opt_msgsize);
722	} else if (strcmp(test_name, "blocked_read") == 0) {
723		rc = blocked_read_test(opt_repeat);
724	} else if (strcmp(test_name, "closer1") == 0) {
725		rc = closer1_test(opt_repeat);
726	} else if (strcmp(test_name, "closer2") == 0) {
727		rc = closer2_test(opt_repeat);
728	} else if (strcmp(test_name, "closer3") == 0) {
729		rc = closer3_test(opt_repeat);
730	} else if (strcmp(test_name, "echo") == 0) {
731		rc = echo_test(opt_repeat, opt_msgsize, opt_variable);
732	} else if(strcmp(test_name, "ta2ta-ipc") == 0) {
733		rc = ta2ta_ipc_test();
734	} else if (strcmp(test_name, "dev-uuid") == 0) {
735		rc = dev_uuid_test();
736	} else if (strcmp(test_name, "ta-access") == 0) {
737		rc = ta_access_test();
738	} else {
739		fprintf(stderr, "Unrecognized test name '%s'\n", test_name);
740		print_usage_and_exit(argv[0], EXIT_FAILURE, true);
741	}
742
743	return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
744}
745