master
hxx 127 lines 3.46 KB
Raw
1 // Copyright (C) Microsoft Corporation. All rights reserved.
2 #pragma once
3
4 /*
5 * This class contains the implementation for the Syscall() wrapper.
6 * This wrapper automatically throws a detailed exception if the
7 * underlying call fails.
8 * Example exception message:
9 * what(): Exception thrown by NetlinkChannel in ./NetlinkChannel.hxx:18 :
10 * socket(16, 1248, 0) failed with errno=22 (Invalid argument)
11 */
12
13 #include <sys/ioctl.h>
14 #include <sys/prctl.h>
15 #include <arpa/inet.h>
16 #include <type_traits>
17 #include <sys/xattr.h>
18 #include <sys/stat.h>
19 #include <sys/epoll.h>
20 #include <unistd.h>
21 #include <fcntl.h>
22 #include <poll.h>
23 #include <string>
24 #include <sstream>
25 #include <map>
26 #include <cassert>
27 #include "SyscallError.h"
28 #include "Utils.h"
29
30 #define X(Method) {(void*)&Method, #Method}
31
32 namespace detail {
33 static const std::map<const void*, const char*> syscalls{
34 X(bind), X(ioctl), X(socket), X(inet_pton), X(send), X(sendto), X(recv), X(sendto),
35 X(recvfrom), X(recvmsg), X(read), X(lseek), X(open), X(prctl), X(fork), X(execl),
36 X(poll), X(pipe), X(socketpair), X(readlink), X(getxattr), X(dup), X(write), X(pipe2),
37 X(syscall), X(stat), X(epoll_create1), X(epoll_ctl), X(epoll_wait), X(listen), X(accept4)};
38 #undef X
39
40 inline std::string ArgumentToString(const std::nullptr_t&)
41 {
42 return "nullptr";
43 }
44
45 template <typename T>
46 std::string ArgumentToString(T* arg)
47 {
48 std::stringstream output;
49 if constexpr (std::is_class_v<T>)
50 {
51 utils::FormatBinary(output, arg, sizeof(*arg));
52 }
53 else
54 {
55 output << utils::BytesToHex(&arg, sizeof(arg), "");
56 }
57
58 return output.str();
59 }
60
61 template <typename T>
62 std::string ArgumentToString(T arg)
63 {
64 return std::to_string(arg);
65 }
66
67 inline void PrettyPrintArguments(std::ostream&)
68 {
69 }
70
71 template <typename T>
72 void PrettyPrintArguments(std::ostream& out, T first)
73 {
74 out << ArgumentToString(first);
75 }
76
77 template <typename T, typename... Args>
78 void PrettyPrintArguments(std::ostream& out, T first, Args... args)
79 {
80 out << ArgumentToString(first) << ", ";
81 PrettyPrintArguments(out, std::forward<Args>(args)...);
82 }
83 } // namespace detail
84
85 template <typename Routine, typename... Args>
86 typename std::invoke_result<Routine, Args...>::type _Syscall(const std::source_location& source, Routine routine, Args... args)
87 {
88 const auto call = detail::syscalls.find(reinterpret_cast<void*>(routine));
89 assert(call != detail::syscalls.end());
90
91 auto result = routine(std::forward<Args>(args)...);
92 if (result >= 0)
93 {
94 return result;
95 }
96
97 const int savedErrno = errno;
98
99 std::stringstream argString;
100 detail::PrettyPrintArguments(argString, std::forward<Args>(args)...);
101
102 throw SyscallError(call->second, argString.str(), savedErrno, source);
103 }
104
105 template <typename Routine, typename... Args>
106 typename std::invoke_result<Routine, Args...>::type _SyscallInterruptable(const std::source_location& source, Routine routine, Args... args)
107 {
108 const auto call = detail::syscalls.find(reinterpret_cast<void*>(routine));
109 assert(call != detail::syscalls.end());
110
111 auto result = routine(std::forward<Args>(args)...);
112 if (result >= 0)
113 {
114 return result;
115 }
116
117 const int savedErrno = errno;
118 if (savedErrno == EINTR)
119 {
120 return result;
121 }
122
123 std::stringstream argString;
124 detail::PrettyPrintArguments(argString, std::forward<Args>(args)...);
125
126 throw SyscallError(call->second, argString.str(), savedErrno, source);
127 }