-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhostname.cpp
More file actions
137 lines (123 loc) · 3.95 KB
/
Copy pathhostname.cpp
File metadata and controls
137 lines (123 loc) · 3.95 KB
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
135
136
137
// hostname.cpp — show the system hostname. BusyBox-compatible options.
//
// -s short (cut at first dot) -i IPv4 address(es)
// -f FQDN (canonical via getaddrinfo) -d DNS domain (FQDN minus short part)
//
// Setting the hostname (hostname NAME) and sethostname(2) are not in this cut.
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/socket.h>
#include <unistd.h>
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>
#include <cfbox/args.hpp>
#include <cfbox/error.hpp>
#include <cfbox/help.hpp>
namespace {
constexpr cfbox::help::HelpEntry HELP = {
.name = "hostname",
.version = CFBOX_VERSION_STRING,
.one_line = "show or set the system host name",
.usage = "hostname [-s|-i|-f|-d]",
.options = " -s short hostname (cut at first dot)\n"
" -i IP address(es) for the host name\n"
" -f long host name (FQDN)\n"
" -d DNS domain name",
.extra = "",
};
auto get_hostname() -> std::string {
char buf[256] = {};
if (gethostname(buf, sizeof(buf) - 1) != 0)
return {};
return buf;
}
// Resolve name → IPv4 dotted strings. memcpy ai_addr to avoid -Wcast-align.
auto resolve_ipv4(const std::string& name) -> std::vector<std::string> {
addrinfo hints{};
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
addrinfo* res = nullptr;
if (getaddrinfo(name.c_str(), nullptr, &hints, &res) != 0)
return {};
std::vector<std::string> out;
for (addrinfo* p = res; p; p = p->ai_next) {
sockaddr_in sa;
std::memcpy(&sa, p->ai_addr, sizeof(sa));
char buf[INET_ADDRSTRLEN];
if (inet_ntop(AF_INET, &sa.sin_addr, buf, sizeof(buf)))
out.push_back(buf);
}
freeaddrinfo(res);
return out;
}
// Canonical FQDN via getaddrinfo(AI_CANONNAME); falls back to gethostname().
auto resolve_fqdn(const std::string& name) -> std::string {
addrinfo hints{};
hints.ai_family = AF_INET;
hints.ai_flags = AI_CANONNAME;
addrinfo* res = nullptr;
if (getaddrinfo(name.c_str(), nullptr, &hints, &res) != 0 || !res)
return name;
std::string fqdn = res->ai_canonname ? res->ai_canonname : name;
freeaddrinfo(res);
return fqdn;
}
} // namespace
auto hostname_main(int argc, char* argv[]) -> int {
using namespace cfbox;
auto parsed = args::parse(argc, argv,
{
args::OptSpec{'s', false, "short"},
args::OptSpec{'i', false, "ip-address"},
args::OptSpec{'f', false, "fqdn"},
args::OptSpec{'d', false, "domain"},
});
if (parsed.has_long("help")) {
help::print_help(HELP);
return 0;
}
if (parsed.has_long("version")) {
help::print_version(HELP);
return 0;
}
std::string name = get_hostname();
if (name.empty()) {
CFBOX_ERR("hostname", "cannot get hostname");
return 1;
}
if (parsed.has('i')) {
auto addrs = resolve_ipv4(name);
if (addrs.empty()) {
CFBOX_ERR("hostname", "cannot resolve %s", name.c_str());
return 1;
}
for (std::size_t k = 0; k < addrs.size(); ++k) {
if (k)
std::putchar(' ');
std::fputs(addrs[k].c_str(), stdout);
}
std::putchar('\n');
return 0;
}
if (parsed.has('f')) {
std::puts(resolve_fqdn(name).c_str());
return 0;
}
if (parsed.has('d')) {
std::string fqdn = resolve_fqdn(name);
auto dot = fqdn.find('.');
if (dot != std::string::npos)
std::puts(fqdn.substr(dot + 1).c_str());
return 0;
}
std::string out = name;
if (parsed.has('s')) {
auto dot = out.find('.');
if (dot != std::string::npos)
out.erase(dot);
}
std::puts(out.c_str());
return 0;
}