162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * 	ucon.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2004+ Evgeniy Polyakov <zbr@ioremap.net>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <asm/types.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <sys/types.h>
1162306a36Sopenharmony_ci#include <sys/socket.h>
1262306a36Sopenharmony_ci#include <sys/poll.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/netlink.h>
1562306a36Sopenharmony_ci#include <linux/rtnetlink.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <arpa/inet.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <stdbool.h>
2062306a36Sopenharmony_ci#include <stdio.h>
2162306a36Sopenharmony_ci#include <stdlib.h>
2262306a36Sopenharmony_ci#include <unistd.h>
2362306a36Sopenharmony_ci#include <string.h>
2462306a36Sopenharmony_ci#include <errno.h>
2562306a36Sopenharmony_ci#include <time.h>
2662306a36Sopenharmony_ci#include <getopt.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include <linux/connector.h>
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define DEBUG
3162306a36Sopenharmony_ci#define NETLINK_CONNECTOR 	11
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/* Hopefully your userspace connector.h matches this kernel */
3462306a36Sopenharmony_ci#define CN_TEST_IDX		CN_NETLINK_USERS + 3
3562306a36Sopenharmony_ci#define CN_TEST_VAL		0x456
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#ifdef DEBUG
3862306a36Sopenharmony_ci#define ulog(f, a...) fprintf(stdout, f, ##a)
3962306a36Sopenharmony_ci#else
4062306a36Sopenharmony_ci#define ulog(f, a...) do {} while (0)
4162306a36Sopenharmony_ci#endif
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic int need_exit;
4462306a36Sopenharmony_cistatic __u32 seq;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic int netlink_send(int s, struct cn_msg *msg)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	struct nlmsghdr *nlh;
4962306a36Sopenharmony_ci	unsigned int size;
5062306a36Sopenharmony_ci	int err;
5162306a36Sopenharmony_ci	char buf[128];
5262306a36Sopenharmony_ci	struct cn_msg *m;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	size = NLMSG_SPACE(sizeof(struct cn_msg) + msg->len);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	nlh = (struct nlmsghdr *)buf;
5762306a36Sopenharmony_ci	nlh->nlmsg_seq = seq++;
5862306a36Sopenharmony_ci	nlh->nlmsg_pid = getpid();
5962306a36Sopenharmony_ci	nlh->nlmsg_type = NLMSG_DONE;
6062306a36Sopenharmony_ci	nlh->nlmsg_len = size;
6162306a36Sopenharmony_ci	nlh->nlmsg_flags = 0;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	m = NLMSG_DATA(nlh);
6462306a36Sopenharmony_ci#if 0
6562306a36Sopenharmony_ci	ulog("%s: [%08x.%08x] len=%u, seq=%u, ack=%u.\n",
6662306a36Sopenharmony_ci	       __func__, msg->id.idx, msg->id.val, msg->len, msg->seq, msg->ack);
6762306a36Sopenharmony_ci#endif
6862306a36Sopenharmony_ci	memcpy(m, msg, sizeof(*m) + msg->len);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	err = send(s, nlh, size, 0);
7162306a36Sopenharmony_ci	if (err == -1)
7262306a36Sopenharmony_ci		ulog("Failed to send: %s [%d].\n",
7362306a36Sopenharmony_ci			strerror(errno), errno);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	return err;
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic void usage(void)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	printf(
8162306a36Sopenharmony_ci		"Usage: ucon [options] [output file]\n"
8262306a36Sopenharmony_ci		"\n"
8362306a36Sopenharmony_ci		"\t-h\tthis help screen\n"
8462306a36Sopenharmony_ci		"\t-s\tsend buffers to the test module\n"
8562306a36Sopenharmony_ci		"\n"
8662306a36Sopenharmony_ci		"The default behavior of ucon is to subscribe to the test module\n"
8762306a36Sopenharmony_ci		"and wait for state messages.  Any ones received are dumped to the\n"
8862306a36Sopenharmony_ci		"specified output file (or stdout).  The test module is assumed to\n"
8962306a36Sopenharmony_ci		"have an id of {%u.%u}\n"
9062306a36Sopenharmony_ci		"\n"
9162306a36Sopenharmony_ci		"If you get no output, then verify the cn_test module id matches\n"
9262306a36Sopenharmony_ci		"the expected id above.\n"
9362306a36Sopenharmony_ci		, CN_TEST_IDX, CN_TEST_VAL
9462306a36Sopenharmony_ci	);
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ciint main(int argc, char *argv[])
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	int s;
10062306a36Sopenharmony_ci	char buf[1024];
10162306a36Sopenharmony_ci	int len;
10262306a36Sopenharmony_ci	struct nlmsghdr *reply;
10362306a36Sopenharmony_ci	struct sockaddr_nl l_local;
10462306a36Sopenharmony_ci	struct cn_msg *data;
10562306a36Sopenharmony_ci	FILE *out;
10662306a36Sopenharmony_ci	time_t tm;
10762306a36Sopenharmony_ci	struct pollfd pfd;
10862306a36Sopenharmony_ci	bool send_msgs = false;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	while ((s = getopt(argc, argv, "hs")) != -1) {
11162306a36Sopenharmony_ci		switch (s) {
11262306a36Sopenharmony_ci		case 's':
11362306a36Sopenharmony_ci			send_msgs = true;
11462306a36Sopenharmony_ci			break;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci		case 'h':
11762306a36Sopenharmony_ci			usage();
11862306a36Sopenharmony_ci			return 0;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci		default:
12162306a36Sopenharmony_ci			/* getopt() outputs an error for us */
12262306a36Sopenharmony_ci			usage();
12362306a36Sopenharmony_ci			return 1;
12462306a36Sopenharmony_ci		}
12562306a36Sopenharmony_ci	}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	if (argc != optind) {
12862306a36Sopenharmony_ci		out = fopen(argv[optind], "a+");
12962306a36Sopenharmony_ci		if (!out) {
13062306a36Sopenharmony_ci			ulog("Unable to open %s for writing: %s\n",
13162306a36Sopenharmony_ci				argv[1], strerror(errno));
13262306a36Sopenharmony_ci			out = stdout;
13362306a36Sopenharmony_ci		}
13462306a36Sopenharmony_ci	} else
13562306a36Sopenharmony_ci		out = stdout;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	memset(buf, 0, sizeof(buf));
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
14062306a36Sopenharmony_ci	if (s == -1) {
14162306a36Sopenharmony_ci		perror("socket");
14262306a36Sopenharmony_ci		return -1;
14362306a36Sopenharmony_ci	}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	l_local.nl_family = AF_NETLINK;
14662306a36Sopenharmony_ci	l_local.nl_groups = -1; /* bitmask of requested groups */
14762306a36Sopenharmony_ci	l_local.nl_pid = 0;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	ulog("subscribing to %u.%u\n", CN_TEST_IDX, CN_TEST_VAL);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	if (bind(s, (struct sockaddr *)&l_local, sizeof(struct sockaddr_nl)) == -1) {
15262306a36Sopenharmony_ci		perror("bind");
15362306a36Sopenharmony_ci		close(s);
15462306a36Sopenharmony_ci		return -1;
15562306a36Sopenharmony_ci	}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci#if 0
15862306a36Sopenharmony_ci	{
15962306a36Sopenharmony_ci		int on = 0x57; /* Additional group number */
16062306a36Sopenharmony_ci		setsockopt(s, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &on, sizeof(on));
16162306a36Sopenharmony_ci	}
16262306a36Sopenharmony_ci#endif
16362306a36Sopenharmony_ci	if (send_msgs) {
16462306a36Sopenharmony_ci		int i, j;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci		memset(buf, 0, sizeof(buf));
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci		data = (struct cn_msg *)buf;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci		data->id.idx = CN_TEST_IDX;
17162306a36Sopenharmony_ci		data->id.val = CN_TEST_VAL;
17262306a36Sopenharmony_ci		data->seq = seq++;
17362306a36Sopenharmony_ci		data->ack = 0;
17462306a36Sopenharmony_ci		data->len = 0;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci		for (j=0; j<10; ++j) {
17762306a36Sopenharmony_ci			for (i=0; i<1000; ++i) {
17862306a36Sopenharmony_ci				len = netlink_send(s, data);
17962306a36Sopenharmony_ci			}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci			ulog("%d messages have been sent to %08x.%08x.\n", i, data->id.idx, data->id.val);
18262306a36Sopenharmony_ci		}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci		return 0;
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	pfd.fd = s;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	while (!need_exit) {
19162306a36Sopenharmony_ci		pfd.events = POLLIN;
19262306a36Sopenharmony_ci		pfd.revents = 0;
19362306a36Sopenharmony_ci		switch (poll(&pfd, 1, -1)) {
19462306a36Sopenharmony_ci			case 0:
19562306a36Sopenharmony_ci				need_exit = 1;
19662306a36Sopenharmony_ci				break;
19762306a36Sopenharmony_ci			case -1:
19862306a36Sopenharmony_ci				if (errno != EINTR) {
19962306a36Sopenharmony_ci					need_exit = 1;
20062306a36Sopenharmony_ci					break;
20162306a36Sopenharmony_ci				}
20262306a36Sopenharmony_ci				continue;
20362306a36Sopenharmony_ci		}
20462306a36Sopenharmony_ci		if (need_exit)
20562306a36Sopenharmony_ci			break;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci		memset(buf, 0, sizeof(buf));
20862306a36Sopenharmony_ci		len = recv(s, buf, sizeof(buf), 0);
20962306a36Sopenharmony_ci		if (len == -1) {
21062306a36Sopenharmony_ci			perror("recv buf");
21162306a36Sopenharmony_ci			close(s);
21262306a36Sopenharmony_ci			return -1;
21362306a36Sopenharmony_ci		}
21462306a36Sopenharmony_ci		reply = (struct nlmsghdr *)buf;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci		switch (reply->nlmsg_type) {
21762306a36Sopenharmony_ci		case NLMSG_ERROR:
21862306a36Sopenharmony_ci			fprintf(out, "Error message received.\n");
21962306a36Sopenharmony_ci			fflush(out);
22062306a36Sopenharmony_ci			break;
22162306a36Sopenharmony_ci		case NLMSG_DONE:
22262306a36Sopenharmony_ci			data = (struct cn_msg *)NLMSG_DATA(reply);
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci			time(&tm);
22562306a36Sopenharmony_ci			fprintf(out, "%.24s : [%x.%x] [%08u.%08u].\n",
22662306a36Sopenharmony_ci				ctime(&tm), data->id.idx, data->id.val, data->seq, data->ack);
22762306a36Sopenharmony_ci			fflush(out);
22862306a36Sopenharmony_ci			break;
22962306a36Sopenharmony_ci		default:
23062306a36Sopenharmony_ci			break;
23162306a36Sopenharmony_ci		}
23262306a36Sopenharmony_ci	}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	close(s);
23562306a36Sopenharmony_ci	return 0;
23662306a36Sopenharmony_ci}
237