~nabijaczleweli/febug

febug/febug.cpp -rw-r--r-- 3.9 KiB
6c7bde18наб Add MacOS PDF manual link. Fix extra space in febug.8 --{help,version} 8 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// SPDX-License-Identifier: MIT


#include <libfebug.hpp>

#include <sys/un.h>

#include <errno.h>
#include <inttypes.h>
#include <unistd.h>

#include <cstdlib>
#include <cstring>

#if __APPLE__
// GUESS WHAT?? DARWIN DEFINES SOCK_SEQPACKET BUT DOESN'T ACTUALLY FUCKING SUPPORT IT (i.e. socket(2) returns EPROTONOSUPPORT). WHY? BECAUSE FUCK YOU.
#undef SOCK_SEQPACKET
#define SOCK_SEQPACKET SOCK_STREAM
#endif


febug::controlled_socket::controlled_socket(const char * path) noexcept {
	if(path && std::getenv("FEBUG_DONT"))
		return;

	if((this->fd = socket(AF_UNIX, SOCK_SEQPACKET, 0)) == -1) {
		std::fprintf(stderr, "febug::controlled_socket: socket: %s\n", std::strerror(errno));
		return;
	}

	if(auto pe = std::getenv("FEBUG_SOCKET"))
		path = pe;

	sockaddr_un addr{};
	addr.sun_family = AF_UNIX;
	strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
	if(connect(this->fd, (const sockaddr *)&addr, sizeof(addr)) == -1) {
		std::fprintf(stderr, "febug::controlled_socket: connect: %s\n", std::strerror(errno));
		close(this->fd);
		this->fd = -1;
		return;
	}

#if __linux__ || __OpenBSD__ || __APPLE__
	// Handled automatically with SO_PASSCRED, also the manual variant didn't work for some reason
	// Only way is getsockopt(SO_PEERCRED)
	// Only way is getsockopt(LOCAL_PEERCRED)+getsockopt(LOCAL_PEEREPID)
#elif __NetBSD__
	// Correct way is automatically via LOCAL_CREDS
	// However, the message /must/ be sent after the peer sets it; use a sync message from the server for this,
	// otherwise we sent the first febug_message too quickly sometimes
	attn_febug_message sync_msg{};
	if(recv(this->fd, &sync_msg, sizeof(sync_msg), 0) != sizeof(sync_msg)) {
		std::fprintf(stderr, "febug::controlled_socket: recv: %s\n", std::strerror(errno));
		close(this->fd);
		this->fd = -1;
		return;
	}
#else
	// From FreeBSD 12.1-RELEASE-p7 /usr/include/socket.h:
	/*
	 * Credentials structure, used to verify the identity of a peer
	 * process that has sent us a message. This is allocated by the
	 * peer process but filled in by the kernel. This prevents the
	 * peer from lying about its identity. (Note that cmcred_groups[0]
	 * is the effective GID.)
	 */
	char cmsgbuf[CMSG_SPACE(sizeof(cmsgcred))];

	msghdr msg{};
	msg.msg_control    = cmsgbuf;
	msg.msg_controllen = sizeof(cmsgbuf);

	auto cmsg          = CMSG_FIRSTHDR(&msg);
	cmsg->cmsg_level   = SOL_SOCKET;
	cmsg->cmsg_type    = SCM_CREDS;
	cmsg->cmsg_len     = CMSG_LEN(sizeof(cmsgcred));
	msg.msg_controllen = cmsg->cmsg_len;  // total size of all control blocks

	if((sendmsg(this->fd, &msg, 0)) == -1) {
		fprintf(stderr, "febug::controlled_socket: sendmsg: %m\n");
		close(this->fd);
		this->fd = -1;
		return;
	}
#endif
}

febug::controlled_socket::~controlled_socket() noexcept {
	if(this->fd != -1) {
		close(this->fd);
		this->fd = -1;
	}
}

febug::controlled_socket febug::global_controlled_socket;


std::map<std::size_t, void (*)(int, std::size_t)> febug::formatters;

void febug::debug_handler(int) noexcept {
	if(global_controlled_socket == -1)
		return;

	attn_febug_message afmsg{};
	iovec buf_i{&afmsg, sizeof(afmsg)};

	int retpipe = -1;
	char cmsgbuf[CMSG_SPACE(sizeof(retpipe))];

	msghdr msg{};
	msg.msg_iov        = &buf_i;
	msg.msg_iovlen     = 1;
	msg.msg_control    = cmsgbuf;
	msg.msg_controllen = sizeof(cmsgbuf);

	if(recvmsg(global_controlled_socket, &msg, 0) == -1) {
		std::fprintf(stderr, "recvmsg: %s\n", std::strerror(errno));
		return;
	}

	if(auto cmsg = CMSG_FIRSTHDR(&msg); cmsg != nullptr && cmsg->cmsg_type == SCM_RIGHTS) {
		memcpy(&retpipe, CMSG_DATA(cmsg), sizeof(retpipe));
		// std::cerr << "got " << retpipe << ": " << afmsg.variable_id << ", " << afmsg.variable_type << '\n';

		if(const auto itr = formatters.find(afmsg.variable_type); itr != formatters.end())
			itr->second(retpipe, afmsg.variable_id);
		else
			dprintf(retpipe, "Unknown variable type %" PRIu64 " with ID %" PRIu64 "\n", afmsg.variable_type, afmsg.variable_id);

		close(retpipe);
	} else
		std::fprintf(stderr, "febug::debug_handler: cmsg: no fd\n");
}