1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "ueventd_parser.h"
18 
19 #include <grp.h>
20 #include <pwd.h>
21 
22 #include "keyword_map.h"
23 
24 namespace android {
25 namespace init {
26 
ParsePermissionsLine(std::vector<std::string> && args,std::vector<SysfsPermissions> * out_sysfs_permissions,std::vector<Permissions> * out_dev_permissions)27 Result<Success> ParsePermissionsLine(std::vector<std::string>&& args,
28                                      std::vector<SysfsPermissions>* out_sysfs_permissions,
29                                      std::vector<Permissions>* out_dev_permissions) {
30     bool is_sysfs = out_sysfs_permissions != nullptr;
31     if (is_sysfs && args.size() != 5) {
32         return Error() << "/sys/ lines must have 5 entries";
33     }
34 
35     if (!is_sysfs && args.size() != 4) {
36         return Error() << "/dev/ lines must have 4 entries";
37     }
38 
39     auto it = args.begin();
40     const std::string& name = *it++;
41 
42     std::string sysfs_attribute;
43     if (is_sysfs) sysfs_attribute = *it++;
44 
45     // args is now common to both sys and dev entries and contains: <perm> <uid> <gid>
46     std::string& perm_string = *it++;
47     char* end_pointer = 0;
48     mode_t perm = strtol(perm_string.c_str(), &end_pointer, 8);
49     if (end_pointer == nullptr || *end_pointer != '\0') {
50         return Error() << "invalid mode '" << perm_string << "'";
51     }
52 
53     std::string& uid_string = *it++;
54     passwd* pwd = getpwnam(uid_string.c_str());
55     if (!pwd) {
56         return Error() << "invalid uid '" << uid_string << "'";
57     }
58     uid_t uid = pwd->pw_uid;
59 
60     std::string& gid_string = *it++;
61     struct group* grp = getgrnam(gid_string.c_str());
62     if (!grp) {
63         return Error() << "invalid gid '" << gid_string << "'";
64     }
65     gid_t gid = grp->gr_gid;
66 
67     if (is_sysfs) {
68         out_sysfs_permissions->emplace_back(name, sysfs_attribute, perm, uid, gid);
69     } else {
70         out_dev_permissions->emplace_back(name, perm, uid, gid);
71     }
72     return Success();
73 }
74 
ParseSection(std::vector<std::string> && args,const std::string & filename,int line)75 Result<Success> SubsystemParser::ParseSection(std::vector<std::string>&& args,
76                                               const std::string& filename, int line) {
77     if (args.size() != 2) {
78         return Error() << "subsystems must have exactly one name";
79     }
80 
81     if (std::find(subsystems_->begin(), subsystems_->end(), args[1]) != subsystems_->end()) {
82         return Error() << "ignoring duplicate subsystem entry";
83     }
84 
85     subsystem_ = Subsystem(std::move(args[1]));
86 
87     return Success();
88 }
89 
ParseDevName(std::vector<std::string> && args)90 Result<Success> SubsystemParser::ParseDevName(std::vector<std::string>&& args) {
91     if (args[1] == "uevent_devname") {
92         subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVNAME;
93         return Success();
94     }
95     if (args[1] == "uevent_devpath") {
96         subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVPATH;
97         return Success();
98     }
99 
100     return Error() << "invalid devname '" << args[1] << "'";
101 }
102 
ParseDirName(std::vector<std::string> && args)103 Result<Success> SubsystemParser::ParseDirName(std::vector<std::string>&& args) {
104     if (args[1].front() != '/') {
105         return Error() << "dirname '" << args[1] << " ' does not start with '/'";
106     }
107 
108     subsystem_.dir_name_ = args[1];
109     return Success();
110 }
111 
ParseLineSection(std::vector<std::string> && args,int line)112 Result<Success> SubsystemParser::ParseLineSection(std::vector<std::string>&& args, int line) {
113     using OptionParser = Result<Success> (SubsystemParser::*)(std::vector<std::string> && args);
114 
115     static class OptionParserMap : public KeywordMap<OptionParser> {
116       private:
117         const Map& map() const override {
118             // clang-format off
119             static const Map option_parsers = {
120                 {"devname",     {1,     1,      &SubsystemParser::ParseDevName}},
121                 {"dirname",     {1,     1,      &SubsystemParser::ParseDirName}},
122             };
123             // clang-format on
124             return option_parsers;
125         }
126     } parser_map;
127 
128     auto parser = parser_map.FindFunction(args);
129 
130     if (!parser) return Error() << parser.error();
131 
132     return std::invoke(*parser, this, std::move(args));
133 }
134 
EndSection()135 Result<Success> SubsystemParser::EndSection() {
136     subsystems_->emplace_back(std::move(subsystem_));
137 
138     return Success();
139 }
140 
141 }  // namespace init
142 }  // namespace android
143