Service.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. /*
  2. * Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org>
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright notice, this
  9. * list of conditions and the following disclaimer.
  10. *
  11. * 2. Redistributions in binary form must reproduce the above copyright notice,
  12. * this list of conditions and the following disclaimer in the documentation
  13. * and/or other materials provided with the distribution.
  14. *
  15. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  16. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  18. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  19. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  20. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  21. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  22. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  23. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  24. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. */
  26. #include "Service.h"
  27. #include <AK/Debug.h>
  28. #include <AK/HashMap.h>
  29. #include <AK/JsonArray.h>
  30. #include <AK/JsonObject.h>
  31. #include <LibCore/ConfigFile.h>
  32. #include <LibCore/File.h>
  33. #include <LibCore/Socket.h>
  34. #include <fcntl.h>
  35. #include <grp.h>
  36. #include <libgen.h>
  37. #include <pwd.h>
  38. #include <sched.h>
  39. #include <stdio.h>
  40. #include <sys/ioctl.h>
  41. #include <unistd.h>
  42. static HashMap<pid_t, Service*> s_service_map;
  43. Service* Service::find_by_pid(pid_t pid)
  44. {
  45. auto it = s_service_map.find(pid);
  46. if (it == s_service_map.end())
  47. return nullptr;
  48. return (*it).value;
  49. }
  50. void Service::setup_socket()
  51. {
  52. VERIFY(!m_socket_path.is_null());
  53. VERIFY(m_socket_fd == -1);
  54. auto ok = Core::File::ensure_parent_directories(m_socket_path);
  55. VERIFY(ok);
  56. // Note: we use SOCK_CLOEXEC here to make sure we don't leak every socket to
  57. // all the clients. We'll make the one we do need to pass down !CLOEXEC later
  58. // after forking off the process.
  59. m_socket_fd = socket(AF_LOCAL, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
  60. if (m_socket_fd < 0) {
  61. perror("socket");
  62. VERIFY_NOT_REACHED();
  63. }
  64. if (m_account.has_value()) {
  65. auto& account = m_account.value();
  66. if (fchown(m_socket_fd, account.uid(), account.gid()) < 0) {
  67. perror("fchown");
  68. VERIFY_NOT_REACHED();
  69. }
  70. }
  71. if (fchmod(m_socket_fd, m_socket_permissions) < 0) {
  72. perror("fchmod");
  73. VERIFY_NOT_REACHED();
  74. }
  75. auto socket_address = Core::SocketAddress::local(m_socket_path);
  76. auto un_optional = socket_address.to_sockaddr_un();
  77. if (!un_optional.has_value()) {
  78. dbgln("Socket name {} is too long. BUG! This should have failed earlier!", m_socket_path);
  79. VERIFY_NOT_REACHED();
  80. }
  81. auto un = un_optional.value();
  82. int rc = bind(m_socket_fd, (const sockaddr*)&un, sizeof(un));
  83. if (rc < 0) {
  84. perror("bind");
  85. VERIFY_NOT_REACHED();
  86. }
  87. rc = listen(m_socket_fd, 16);
  88. if (rc < 0) {
  89. perror("listen");
  90. VERIFY_NOT_REACHED();
  91. }
  92. }
  93. void Service::setup_notifier()
  94. {
  95. VERIFY(m_lazy);
  96. VERIFY(m_socket_fd >= 0);
  97. VERIFY(!m_socket_notifier);
  98. m_socket_notifier = Core::Notifier::construct(m_socket_fd, Core::Notifier::Event::Read, this);
  99. m_socket_notifier->on_ready_to_read = [this] {
  100. handle_socket_connection();
  101. };
  102. }
  103. void Service::handle_socket_connection()
  104. {
  105. dbgln_if(SERVICE_DEBUG, "Ready to read on behalf of {}", name());
  106. if (m_accept_socket_connections) {
  107. int accepted_fd = accept(m_socket_fd, nullptr, nullptr);
  108. if (accepted_fd < 0) {
  109. perror("accept");
  110. return;
  111. }
  112. spawn(accepted_fd);
  113. close(accepted_fd);
  114. } else {
  115. remove_child(*m_socket_notifier);
  116. m_socket_notifier = nullptr;
  117. spawn(m_socket_fd);
  118. }
  119. }
  120. void Service::activate()
  121. {
  122. VERIFY(m_pid < 0);
  123. if (m_lazy)
  124. setup_notifier();
  125. else
  126. spawn(m_socket_fd);
  127. }
  128. void Service::spawn(int socket_fd)
  129. {
  130. dbgln_if(SERVICE_DEBUG, "Spawning {}", name());
  131. m_run_timer.start();
  132. pid_t pid = fork();
  133. if (pid < 0) {
  134. perror("fork");
  135. dbgln("Failed to spawn {}. Sucks, dude :(", name());
  136. } else if (pid == 0) {
  137. // We are the child.
  138. if (!m_working_directory.is_null()) {
  139. if (chdir(m_working_directory.characters()) < 0) {
  140. perror("chdir");
  141. VERIFY_NOT_REACHED();
  142. }
  143. }
  144. struct sched_param p;
  145. p.sched_priority = m_priority;
  146. int rc = sched_setparam(0, &p);
  147. if (rc < 0) {
  148. perror("sched_setparam");
  149. VERIFY_NOT_REACHED();
  150. }
  151. if (!m_stdio_file_path.is_null()) {
  152. close(STDIN_FILENO);
  153. int fd = open(m_stdio_file_path.characters(), O_RDWR, 0);
  154. VERIFY(fd <= 0);
  155. if (fd < 0) {
  156. perror("open");
  157. VERIFY_NOT_REACHED();
  158. }
  159. dup2(STDIN_FILENO, STDOUT_FILENO);
  160. dup2(STDIN_FILENO, STDERR_FILENO);
  161. if (isatty(STDIN_FILENO)) {
  162. ioctl(STDIN_FILENO, TIOCSCTTY);
  163. }
  164. } else {
  165. if (isatty(STDIN_FILENO)) {
  166. ioctl(STDIN_FILENO, TIOCNOTTY);
  167. }
  168. close(STDIN_FILENO);
  169. close(STDOUT_FILENO);
  170. close(STDERR_FILENO);
  171. int fd = open("/dev/null", O_RDWR);
  172. VERIFY(fd == STDIN_FILENO);
  173. dup2(STDIN_FILENO, STDOUT_FILENO);
  174. dup2(STDIN_FILENO, STDERR_FILENO);
  175. }
  176. if (socket_fd >= 0) {
  177. VERIFY(!m_socket_path.is_null());
  178. VERIFY(socket_fd > 3);
  179. dup2(socket_fd, 3);
  180. // The new descriptor is !CLOEXEC here.
  181. setenv("SOCKET_TAKEOVER", "1", true);
  182. }
  183. if (m_account.has_value()) {
  184. auto& account = m_account.value();
  185. if (setgid(account.gid()) < 0 || setgroups(account.extra_gids().size(), account.extra_gids().data()) < 0 || setuid(account.uid()) < 0) {
  186. dbgln("Failed to drop privileges (GID={}, UID={})\n", account.gid(), account.uid());
  187. exit(1);
  188. }
  189. setenv("HOME", account.home_directory().characters(), true);
  190. }
  191. for (String& env : m_environment)
  192. putenv(const_cast<char*>(env.characters()));
  193. char* argv[m_extra_arguments.size() + 2];
  194. argv[0] = const_cast<char*>(m_executable_path.characters());
  195. for (size_t i = 0; i < m_extra_arguments.size(); i++)
  196. argv[i + 1] = const_cast<char*>(m_extra_arguments[i].characters());
  197. argv[m_extra_arguments.size() + 1] = nullptr;
  198. rc = execv(argv[0], argv);
  199. perror("exec");
  200. VERIFY_NOT_REACHED();
  201. } else if (!m_multi_instance) {
  202. // We are the parent.
  203. m_pid = pid;
  204. s_service_map.set(pid, this);
  205. }
  206. }
  207. void Service::did_exit(int exit_code)
  208. {
  209. VERIFY(m_pid > 0);
  210. VERIFY(!m_multi_instance);
  211. dbgln("Service {} has exited with exit code {}", name(), exit_code);
  212. s_service_map.remove(m_pid);
  213. m_pid = -1;
  214. if (!m_keep_alive)
  215. return;
  216. int run_time_in_msec = m_run_timer.elapsed();
  217. bool exited_successfully = exit_code == 0;
  218. if (!exited_successfully && run_time_in_msec < 1000) {
  219. switch (m_restart_attempts) {
  220. case 0:
  221. dbgln("Trying again");
  222. break;
  223. case 1:
  224. dbgln("Third time's a charm?");
  225. break;
  226. default:
  227. dbgln("Giving up on {}. Good luck!", name());
  228. return;
  229. }
  230. m_restart_attempts++;
  231. }
  232. activate();
  233. }
  234. Service::Service(const Core::ConfigFile& config, const StringView& name)
  235. : Core::Object(nullptr)
  236. {
  237. VERIFY(config.has_group(name));
  238. set_name(name);
  239. m_executable_path = config.read_entry(name, "Executable", String::formatted("/bin/{}", this->name()));
  240. m_extra_arguments = config.read_entry(name, "Arguments", "").split(' ');
  241. m_stdio_file_path = config.read_entry(name, "StdIO");
  242. String prio = config.read_entry(name, "Priority");
  243. if (prio == "low")
  244. m_priority = 10;
  245. else if (prio == "normal" || prio.is_null())
  246. m_priority = 30;
  247. else if (prio == "high")
  248. m_priority = 50;
  249. else
  250. VERIFY_NOT_REACHED();
  251. m_keep_alive = config.read_bool_entry(name, "KeepAlive");
  252. m_lazy = config.read_bool_entry(name, "Lazy");
  253. m_user = config.read_entry(name, "User");
  254. if (!m_user.is_null()) {
  255. auto result = Core::Account::from_name(m_user.characters());
  256. if (result.is_error())
  257. warnln("Failed to resolve user {}: {}", m_user, result.error());
  258. else
  259. m_account = result.value();
  260. }
  261. m_working_directory = config.read_entry(name, "WorkingDirectory");
  262. m_environment = config.read_entry(name, "Environment").split(' ');
  263. m_boot_modes = config.read_entry(name, "BootModes", "graphical").split(',');
  264. m_multi_instance = config.read_bool_entry(name, "MultiInstance");
  265. m_accept_socket_connections = config.read_bool_entry(name, "AcceptSocketConnections");
  266. m_socket_path = config.read_entry(name, "Socket");
  267. // Lazy requires Socket.
  268. VERIFY(!m_lazy || !m_socket_path.is_null());
  269. // AcceptSocketConnections always requires Socket, Lazy, and MultiInstance.
  270. VERIFY(!m_accept_socket_connections || (!m_socket_path.is_null() && m_lazy && m_multi_instance));
  271. // MultiInstance doesn't work with KeepAlive.
  272. VERIFY(!m_multi_instance || !m_keep_alive);
  273. // Socket path (plus NUL) must fit into the structs sent to the Kernel.
  274. VERIFY(m_socket_path.length() < UNIX_PATH_MAX);
  275. if (!m_socket_path.is_null() && is_enabled()) {
  276. auto socket_permissions_string = config.read_entry(name, "SocketPermissions", "0600");
  277. m_socket_permissions = strtol(socket_permissions_string.characters(), nullptr, 8) & 0777;
  278. setup_socket();
  279. }
  280. }
  281. void Service::save_to(JsonObject& json)
  282. {
  283. Core::Object::save_to(json);
  284. json.set("executable_path", m_executable_path);
  285. // FIXME: This crashes Inspector.
  286. /*
  287. JsonArray extra_args;
  288. for (String& arg : m_extra_arguments)
  289. extra_args.append(arg);
  290. json.set("extra_arguments", move(extra_args));
  291. JsonArray boot_modes;
  292. for (String& mode : m_boot_modes)
  293. boot_modes.append(mode);
  294. json.set("boot_modes", boot_modes);
  295. JsonArray environment;
  296. for (String& env : m_environment)
  297. boot_modes.append(env);
  298. json.set("environment", environment);
  299. */
  300. json.set("stdio_file_path", m_stdio_file_path);
  301. json.set("priority", m_priority);
  302. json.set("keep_alive", m_keep_alive);
  303. json.set("socket_path", m_socket_path);
  304. json.set("socket_permissions", m_socket_permissions);
  305. json.set("lazy", m_lazy);
  306. json.set("user", m_user);
  307. json.set("multi_instance", m_multi_instance);
  308. json.set("accept_socket_connections", m_accept_socket_connections);
  309. if (m_pid > 0)
  310. json.set("pid", m_pid);
  311. else
  312. json.set("pid", nullptr);
  313. json.set("restart_attempts", m_restart_attempts);
  314. json.set("working_directory", m_working_directory);
  315. }
  316. bool Service::is_enabled() const
  317. {
  318. extern String g_boot_mode;
  319. return m_boot_modes.contains_slow(g_boot_mode);
  320. }