lxcpp: fix cgroup unit tests
[platform/core/security/vasum.git] / server / server.cpp
1 /*
2  *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *  Contact: Jan Olszak (j.olszak@samsung.com)
5  *
6  *  Licensed under the Apache License, Version 2.0 (the "License");
7  *  you may not use this file except in compliance with the License.
8  *  You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *  Unless required by applicable law or agreed to in writing, software
13  *  distributed under the License is distributed on an "AS IS" BASIS,
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License
17  */
18
19 /**
20  * @file
21  * @author  Jan Olszak (j.olszak@samsung.com)
22  * @brief   Server class definition
23  */
24
25 #include "config.hpp"
26
27 #include "server.hpp"
28 #include "exception.hpp"
29
30 #include "config/manager.hpp"
31 #include "logger/logger.hpp"
32 #include "utils/environment.hpp"
33 #include "utils/fs.hpp"
34 #include "utils/signal.hpp"
35 #include "utils/exception.hpp"
36
37 #include <iostream>
38 #include <csignal>
39 #include <cerrno>
40 #include <string>
41 #include <functional>
42 #include <cstring>
43 #include <unistd.h>
44 #include <pwd.h>
45 #include <sys/stat.h>
46 #include <boost/filesystem.hpp>
47 #include <linux/capability.h>
48
49 #include <sys/types.h>
50 #include <sys/stat.h>
51 #include <fcntl.h>
52 #include <sys/utsname.h>
53 #include <lxc/lxccontainer.h>
54 #include <lxc/version.h>
55
56
57 #ifndef VASUM_USER
58 #error "VASUM_USER must be defined!"
59 #endif
60
61 #ifndef INPUT_EVENT_GROUP
62 #error "INPUT_EVENT_GROUP must be defined!"
63 #endif
64
65 #ifndef DISK_GROUP
66 #error "DISK_GROUP must be defined!"
67 #endif
68
69 #ifndef TTY_GROUP
70 #error "TTY_GROUP must be defined!"
71 #endif
72
73 extern char** environ;
74
75 using namespace utils;
76
77 namespace vasum {
78
79 Server::Server(const std::string& configPath)
80     : mIsRunning(true),
81       mIsUpdate(false),
82       mConfigPath(configPath),
83       mSignalFD(mEventPoll),
84       mZonesManager(mEventPoll, mConfigPath),
85       mDispatchingThread(::pthread_self())
86 {
87     mSignalFD.setHandler(SIGUSR1, std::bind(&Server::handleUpdate, this));
88     mSignalFD.setHandler(SIGINT, std::bind(&Server::handleStop, this));
89     mSignalFD.setHandler(SIGTERM, std::bind(&Server::handleStop, this));
90 }
91
92 void Server::handleUpdate()
93 {
94     LOGD("Received SIGUSR1 - triggering update.");
95     mZonesManager.setZonesDetachOnExit();
96     mZonesManager.stop(false);
97     mIsUpdate = true;
98     mIsRunning = false;
99 }
100
101 void Server::handleStop()
102 {
103     LOGD("Stopping Server");
104     mZonesManager.stop(false);
105     mIsRunning = false;
106 }
107
108 void Server::run(bool asRoot)
109 {
110     if (!prepareEnvironment(mConfigPath, asRoot)) {
111         throw ServerException("Environment setup failed");
112     }
113
114     mZonesManager.start();
115
116     while(mIsRunning || mZonesManager.isRunning()) {
117         mEventPoll.dispatchIteration(-1);
118     }
119 }
120
121 void Server::reloadIfRequired(char* argv[])
122 {
123     if (mIsUpdate) {
124         ::execve(argv[0], argv, environ);
125         LOGE("Failed to reload " << argv[0] << ": " << getSystemErrorMessage());
126     }
127 }
128
129 void Server::terminate()
130 {
131     LOGI("Terminating server");
132     int ret = ::pthread_kill(mDispatchingThread, SIGINT);
133     if(ret != 0) {
134         const std::string msg = "Error during Server termination: " + utils::getSystemErrorMessage(ret);
135         LOGE(msg);
136         throw ServerException(msg);
137     }
138 }
139
140 bool Server::checkEnvironment()
141 {
142     // check kernel
143     struct utsname u;
144     int version, major, minor;
145     ::uname(&u);
146     version = major = minor = 0;
147     ::sscanf(u.release, "%d.%d.%d", &version, &major, &minor);
148     if (version < 2 || (version == 2 && major < 6) || (version == 2 && major == 6 && minor < 29)) {
149         // control-group functionality was merged into kernel version 2.6.24 in 2007 (wikipedia)
150         // namespace support begins from kernels 2.4.19(mnt), 2.6.19(ns,uts,ipc), 2.6.24(pid), 2.6.29(net)
151         // namespace for usr from kernel 3.8(usr) - not used by vasum
152         std::cout << "kernel is old ver=" << u.release << ", run vasum-check-config" << std::endl;
153         return false;
154     }
155     else
156         std::cout << "kernel " << u.release << " [OK]" << std::endl;
157
158     // check lxc (TODO check if running on broken ABI version)
159     if (::strcmp(lxc_get_version(), LXC_VERSION)!=0) {
160         // versions that matters:
161         // 1.1.0 added function ptr 'in-the-middle' destroy_with_snapshots, snapshot_destroy_all (breaks ABI)
162         // 1.1.2 added function ptr 'append' attach_interface,detach_interface,checkpoint,restore (safe for ABI)
163         std::cout << "LXC version not match, compiled for " << LXC_VERSION << ", installed " << lxc_get_version() << std::endl;
164         return false;
165     }
166     else
167         std::cout << "LXC version " << lxc_get_version() << " [OK]" << std::endl;
168
169     // check cgroups (and its subsystems?)
170     std::string cgroupCheck = "/sys/fs/cgroup";
171     int fd = ::open(cgroupCheck.c_str(), O_RDONLY);
172     if (fd == -1) {
173         std::cout << "no cgroups support (can't access " << cgroupCheck << "), run vasum-check-config" << std::endl;
174         return false;
175     }
176
177     bool err = false;
178     std::vector<std::string> cgroupSubsCheck = {"cpu", "cpuset", "memory"};
179     for (std::string f : cgroupSubsCheck)  {
180         if (::faccessat(fd, f.c_str(), R_OK|X_OK, 0) == -1) {
181             std::cout << "no cgroups support (can't access " << cgroupCheck << "/" << f << ")" << std::endl;
182             err=true;
183         }
184     }
185     ::close(fd);
186     if (err) {
187         std::cout << "cgroups problem, run vasum-check-config" << std::endl;
188         return false;
189     }
190     else
191         std::cout << "cgroups support " << " [OK]" << std::endl;
192
193     // check namespaces
194     std::string nsCheck = "/proc/self/ns";
195     if (::access(nsCheck.c_str(), R_OK|X_OK)  == -1) {
196         std::cout << "no namespace support (can't access " << nsCheck << "), run vasum-check-config" << std::endl;
197         return false;
198     }
199     else
200         std::cout << "namespaces support " << " [OK]" << std::endl;
201     ::close(fd);
202
203     return true;
204 }
205
206
207 bool Server::prepareEnvironment(const std::string& configPath, bool runAsRoot)
208 {
209     namespace fs = boost::filesystem;
210
211     // TODO: currently this config is loaded twice: here and in ZonesManager
212     ZonesManagerConfig config;
213     config::loadFromJsonFile(configPath, config);
214
215     struct passwd* pwd = ::getpwnam(VASUM_USER);
216     if (pwd == NULL) {
217         LOGE("getpwnam failed to find user '" << VASUM_USER << "'");
218         return false;
219     }
220     uid_t uid = pwd->pw_uid;
221     gid_t gid = pwd->pw_gid;
222     LOGD("vasum UID = " << uid << ", GID = " << gid);
223
224     // create directory for dbus socket (if needed)
225     if (!config.runMountPointPrefix.empty()) {
226         if (!utils::createDir(config.runMountPointPrefix, uid, gid,
227                               fs::perms::owner_all |
228                               fs::perms::group_read | fs::perms::group_exe |
229                               fs::perms::others_read | fs::perms::others_exe)) {
230             return false;
231         }
232     }
233
234     // Omit supplementaty group setup and root drop if the user is already switched.
235     // This situation will happen during daemon update triggered by SIGUSR1.
236     if (!runAsRoot && geteuid() == uid) {
237         return true;
238     }
239
240     // INPUT_EVENT_GROUP provides access to /dev/input/event* devices used by InputMonitor.
241     // DISK_GROUP provides access to /dev/loop* devices, needed when adding new zone to copy
242     //            zones image
243     if (!utils::setSuppGroups({INPUT_EVENT_GROUP, DISK_GROUP, TTY_GROUP})) {
244         return false;
245     }
246
247     // CAP_SYS_ADMIN allows to mount tmpfs' for dbus communication at the runtime.
248     // NOTE: CAP_MAC_OVERRIDE is temporary and must be removed when "smack namespace"
249     // is introduced. The capability is needed to allow modify SMACK labels of
250     // "/var/run/zones/<zone>/run" mount point.
251     // CAP_SYS_TTY_CONFIG is needed to activate virtual terminals through ioctl calls
252     // CAP_CHOWN is needed when creating new zone from image to set owner/group for each file,
253     // directory or symlink
254     // CAP_SETUID is needed to launch specific funtions as root (see environment.cpp)
255     return (runAsRoot || utils::dropRoot(uid, gid, {CAP_SYS_ADMIN,
256                                          CAP_MAC_OVERRIDE,
257                                          CAP_SYS_TTY_CONFIG,
258                                          CAP_CHOWN,
259                                          CAP_SETUID
260                                                    }));
261 }
262
263
264 } // namespace vasum