1/*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "virtual_device_builder.h"
17
18#include <cerrno>
19#include <csignal>
20#include <cstring>
21#include <fstream>
22#include <iostream>
23#include <regex>
24#include <sstream>
25#include <map>
26
27#include <dirent.h>
28#include <fcntl.h>
29#include <unistd.h>
30#include <sys/ioctl.h>
31#include <sys/stat.h>
32#include <sys/types.h>
33
34#include <securec.h>
35
36#include "devicestatus_define.h"
37#include "fi_log.h"
38#include "if_stream_wrap.h"
39#include "napi_constants.h"
40#include "utility.h"
41#include "virtual_mouse.h"
42#include "virtual_touchscreen.h"
43
44#undef LOG_TAG
45#define LOG_TAG "VirtualDeviceBuilder"
46
47namespace OHOS {
48namespace Msdp {
49namespace DeviceStatus {
50namespace {
51constexpr int32_t MAXIMUM_WAIT_TIME_ALLOWED { 3000 };
52constexpr int32_t MINIMUM_WAIT_TIME_ALLOWED { 5 };
53constexpr ssize_t MAXIMUM_FILESIZE_ALLOWED { 0x100000 };
54} // namespace
55
56VirtualDeviceBuilder::VirtualDeviceBuilder(const std::string &name, uint16_t bustype,
57                                           uint16_t vendor, uint16_t product)
58    : uinputDev_ {
59        .id = {
60            .bustype = bustype,
61            .vendor = vendor,
62            .product = product,
63            .version = 1
64        }
65    }
66{
67    if (strcpy_s(uinputDev_.name, sizeof(uinputDev_.name), name.c_str()) != EOK) {
68        FI_HILOGE("Invalid device name:\'%{public}s\'", name.c_str());
69    }
70}
71
72VirtualDeviceBuilder::VirtualDeviceBuilder(const std::string &name, std::shared_ptr<VirtualDevice> vDev) : vDev_(vDev)
73{
74    CopyProperties(name, vDev);
75}
76
77VirtualDeviceBuilder::~VirtualDeviceBuilder()
78{
79    Close();
80}
81
82void VirtualDeviceBuilder::Daemonize()
83{
84    int32_t fd = fork();
85    if (fd < 0) {
86        exit(EXIT_FAILURE);
87    } else if (fd > 0) {
88        exit(EXIT_SUCCESS);
89    }
90    if (setsid() < 0) {
91        exit(EXIT_SUCCESS);
92    }
93    fd = fork();
94    if (fd < 0) {
95        exit(EXIT_FAILURE);
96    } else if (fd > 0) {
97        exit(EXIT_SUCCESS);
98    }
99    close(STDIN_FILENO);
100    fd = open("/dev/null", O_RDWR);
101    if (fd != STDIN_FILENO) {
102        exit(EXIT_FAILURE);
103    }
104    if (dup2(STDIN_FILENO, STDOUT_FILENO) != STDOUT_FILENO) {
105        exit(EXIT_FAILURE);
106    }
107    if (dup2(STDIN_FILENO, STDERR_FILENO) != STDERR_FILENO) {
108        exit(EXIT_FAILURE);
109    }
110}
111
112void VirtualDeviceBuilder::ConcatenationName(std::string &sLine)
113{
114    auto s = sLine.begin();
115    while (s != sLine.end() && (isspace(*s) || (*s == '\0'))) {
116        s = sLine.erase(s);
117    }
118    while (s != sLine.end()) {
119        while (s != sLine.end() && !isspace(*s) && *s != '\0') {
120            ++s;
121        }
122        auto t = s;
123        while (t != sLine.end() && (isspace(*t) || (*t == '\0'))) {
124            ++t;
125        }
126        if (t != sLine.end()) {
127            *s++ = '_';
128        }
129        while (s != sLine.end() && (isspace(*s) || (*s == '\0'))) {
130            s = sLine.erase(s);
131        }
132    }
133}
134
135bool VirtualDeviceBuilder::ExecuteUnmount(const char *id, const char *name, const std::string &direntName)
136{
137    std::ostringstream sPattern;
138    sPattern << "^vdevadm_(mount|clone)_-t_?" << id;
139    std::regex pattern { sPattern.str() };
140    if (!Utility::IsInteger(direntName)) {
141        return false;
142    }
143
144    std::ostringstream spath;
145    spath << "/proc/" << direntName;
146    struct stat statBuf;
147    if (stat(spath.str().c_str(), &statBuf) != 0) {
148        std::cout << "stat \'" << spath.str() << "\' failed: " << strerror(errno) << std::endl;
149        return false;
150    }
151    if (!S_ISDIR(statBuf.st_mode)) {
152        return false;
153    }
154    spath << "/cmdline";
155    char realPath[PATH_MAX] = { 0 };
156    if (realpath(spath.str().c_str(), realPath) == nullptr) {
157        std::cout << "Invalid path" << spath.str().c_str() << std::endl;
158        return false;
159    }
160    IfStreamWrap fileStream;
161    fileStream.ifStream = std::ifstream(spath.str(), std::ios::in);
162    if (!fileStream.IsOpen()) {
163        return false;
164    }
165    std::string sLine;
166    while (std::getline(fileStream.ifStream, sLine)) {
167        ConcatenationName(sLine);
168        if (std::regex_search(sLine, pattern)) {
169            std::cout << "\tfound: \'" << direntName << "\'" << std::endl;
170            int32_t pid = std::atoi(direntName.c_str());
171            if (kill(static_cast<pid_t>(pid), SIGTERM) != 0) {
172                std::cout << "Failed to stop backing process [" << pid << "]: " << strerror(errno) << std::endl;
173            } else {
174                std::cout << "Unmount virtual " << name << " successfully." << std::endl;
175            }
176            return true;
177        }
178    }
179    return false;
180}
181
182void VirtualDeviceBuilder::Unmount(const char *name, const char *id)
183{
184    std::cout << "Start to unmount virtual " << name << " ..." << std::endl;
185    DIR *procDir = opendir("/proc");
186    if (procDir == nullptr) {
187        std::cout << "Failed to unmount virtual " << name << ": " << strerror(errno) << std::endl;
188        return;
189    }
190
191    struct dirent *dent;
192    while ((dent = readdir(procDir)) != nullptr) {
193        std::string direntName { dent->d_name };
194        if (ExecuteUnmount(id, name, direntName)) {
195            goto EXIT;
196        }
197    }
198    std::cout << "The backing process for virtual " << name << "can't be found." << std::endl;
199EXIT:
200    if (closedir(procDir) != 0) {
201        FI_HILOGE("closedir error:%{public}s", strerror(errno));
202    }
203}
204
205void VirtualDeviceBuilder::SetSupportedEvents()
206{
207    static const std::map<int32_t, std::function<std::vector<uint32_t>()>> uinputTypes {
208        { UI_SET_EVBIT, [this] { return this->GetEventTypes(); } },
209        { UI_SET_KEYBIT, [this] { return this->GetKeys(); } },
210        { UI_SET_PROPBIT, [this] { return this->GetProperties(); } },
211        { UI_SET_ABSBIT, [this] { return this->GetAbs(); } },
212        { UI_SET_RELBIT, [this] { return this->GetRelBits(); } },
213        { UI_SET_MSCBIT, [this] { return this->GetMiscellaneous(); } },
214        { UI_SET_LEDBIT, [this] { return this->GetLeds(); } },
215        { UI_SET_SWBIT, [this] { return this->GetSwitches(); } },
216        { UI_SET_FFBIT, [this] { return this->GetRepeats(); } }
217    };
218
219    for (const auto &setEvents : uinputTypes) {
220        const auto &events = setEvents.second();
221        for (const auto &e : events) {
222            if (ioctl(fd_, setEvents.first, e) < 0) {
223                FI_HILOGE("Failed while setting event type:%{public}s", strerror(errno));
224            }
225        }
226    }
227}
228
229void VirtualDeviceBuilder::SetAbsResolution()
230{
231    for (const auto &item : absInit_) {
232        if (ioctl(fd_, UI_ABS_SETUP, &item) < 0) {
233            FI_HILOGE("Failed while setting abs info:%{public}s", strerror(errno));
234        }
235    }
236}
237
238void VirtualDeviceBuilder::SetPhys()
239{
240    std::string phys;
241
242    if (vDev_ != nullptr) {
243        phys = vDev_->GetPhys();
244    } else {
245        static const std::map<std::string, std::string> mapNames {
246            { "Virtual Mouse", "mouse" },
247            { "Virtual TouchScreen", "touchscreen" },
248            { "Virtual Keyboard", "Keyboard" },
249        };
250        auto tIter = mapNames.find(std::string(uinputDev_.name));
251        if (tIter == mapNames.cend()) {
252            FI_HILOGE("Unrecognized device name");
253            return;
254        }
255        phys = tIter->second;
256        phys.append("/").append(std::to_string(getpid()));
257    }
258
259    if (ioctl(fd_, UI_SET_PHYS, phys.c_str()) < 0) {
260        FI_HILOGE("Failed while setting phys:%{public}s", strerror(errno));
261    }
262}
263
264void VirtualDeviceBuilder::SetIdentity()
265{
266    if (write(fd_, &uinputDev_, sizeof(uinputDev_)) < 0) {
267        FI_HILOGE("Unable to set uinput device info:%{public}s", strerror(errno));
268    }
269}
270
271bool VirtualDeviceBuilder::SetUp()
272{
273    CALL_DEBUG_ENTER;
274    fd_ = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
275    if (fd_ < 0) {
276        FI_HILOGE("Unable to open uinput");
277        return false;
278    }
279
280    SetAbsResolution();
281    SetPhys();
282    SetSupportedEvents();
283    SetIdentity();
284
285    if (ioctl(fd_, UI_DEV_CREATE) < 0) {
286        FI_HILOGE("Failed to setup uinput device");
287        if (close(fd_) != 0) {
288            FI_HILOGE("Close error:%{public}s", strerror(errno));
289        }
290        fd_ = -1;
291        return false;
292    }
293    return true;
294}
295
296void VirtualDeviceBuilder::Close()
297{
298    if (fd_ >= 0) {
299        if (ioctl(fd_, UI_DEV_DESTROY) < 0) {
300            FI_HILOGE("ioctl error:%{public}s", strerror(errno));
301        }
302        if (close(fd_) != 0) {
303            FI_HILOGE("close error:%{public}s", strerror(errno));
304        }
305        fd_ = -1;
306    }
307}
308
309void VirtualDeviceBuilder::SetResolution(const ResolutionInfo &resolutionInfo)
310{
311    uinputAbs_.code = resolutionInfo.axisCode;
312    uinputAbs_.absinfo.resolution = resolutionInfo.absResolution;
313    absInit_.push_back(uinputAbs_);
314}
315
316void VirtualDeviceBuilder::SetAbsValue(const AbsInfo &absInfo)
317{
318    uinputDev_.absmin[absInfo.code] = absInfo.minValue;
319    uinputDev_.absmax[absInfo.code] = absInfo.maxValue;
320    uinputDev_.absfuzz[absInfo.code] = absInfo.fuzz;
321    uinputDev_.absflat[absInfo.code] = absInfo.flat;
322}
323
324const std::vector<uint32_t> &VirtualDeviceBuilder::GetEventTypes() const
325{
326    return eventTypes_;
327}
328
329const std::vector<uint32_t> &VirtualDeviceBuilder::GetKeys() const
330{
331    return keys_;
332}
333
334const std::vector<uint32_t> &VirtualDeviceBuilder::GetProperties() const
335{
336    return properties_;
337}
338
339const std::vector<uint32_t> &VirtualDeviceBuilder::GetAbs() const
340{
341    return abs_;
342}
343
344const std::vector<uint32_t> &VirtualDeviceBuilder::GetRelBits() const
345{
346    return relBits_;
347}
348
349const std::vector<uint32_t> &VirtualDeviceBuilder::GetLeds() const
350{
351    return leds_;
352}
353
354const std::vector<uint32_t> &VirtualDeviceBuilder::GetRepeats() const
355{
356    return repeats_;
357}
358
359const std::vector<uint32_t> &VirtualDeviceBuilder::GetMiscellaneous() const
360{
361    return miscellaneous_;
362}
363
364const std::vector<uint32_t> &VirtualDeviceBuilder::GetSwitches() const
365{
366    return switches_;
367}
368
369void VirtualDeviceBuilder::WaitFor(const char *path, const char *name)
370{
371    CALL_DEBUG_ENTER;
372    CHKPV(path);
373    if (!Utility::IsInteger(std::string(path))) {
374        std::cout << "Invalid argument to \'-w\', time duration of integer type is expected." << std::endl;
375        return;
376    }
377    WaitFor(name, std::atoi(path));
378}
379
380void VirtualDeviceBuilder::WaitFor(const char *name, int32_t timeout)
381{
382    CHKPV(name);
383    if (timeout < MINIMUM_WAIT_TIME_ALLOWED) {
384        std::cout << "Minimum wait time is " << MINIMUM_WAIT_TIME_ALLOWED << ", no wait." << std::endl;
385        return;
386    }
387    if (timeout > MAXIMUM_WAIT_TIME_ALLOWED) {
388        std::cout << "Maximum wait time is " << MAXIMUM_WAIT_TIME_ALLOWED << ", set wait time to this." << std::endl;
389        timeout = MAXIMUM_WAIT_TIME_ALLOWED;
390    }
391    std::cout << "[" << name << "] wait for " << timeout << " milliseconds." << std::endl;
392    std::this_thread::sleep_for(std::chrono::milliseconds(timeout));
393}
394
395int32_t VirtualDeviceBuilder::ReadFile(const char *path, json &model)
396{
397    CALL_DEBUG_ENTER;
398    CHKPR(path, RET_ERR);
399    char realPath[PATH_MAX] {};
400
401    if (realpath(path, realPath) == nullptr) {
402        std::cout << "Invalid path: " << path << std::endl;
403        return RET_ERR;
404    }
405    if (Utility::GetFileSize(realPath) > MAXIMUM_FILESIZE_ALLOWED) {
406        std::cout << "File is too large" << std::endl;
407        return RET_ERR;
408    }
409    std::cout << "Read input data from \'" << realPath << "\'" << std::endl;
410    IfStreamWrap fileStream;
411    fileStream.ifStream = std::ifstream(std::string(realPath));
412    if (!fileStream.IsOpen()) {
413        FI_HILOGE("Could not open the file");
414        return RET_ERR;
415    }
416    model = nlohmann::json::parse(fileStream.ifStream, nullptr, false);
417    if (model.is_discarded()) {
418        FI_HILOGE("model parse failed");
419        return RET_ERR;
420    }
421    return RET_OK;
422}
423
424int32_t VirtualDeviceBuilder::ScanFor(std::function<bool(std::shared_ptr<VirtualDevice>)> pred,
425    std::vector<std::shared_ptr<VirtualDevice>> &vDevs)
426{
427    CALL_DEBUG_ENTER;
428    DIR *dir = opendir(DEV_INPUT_PATH.c_str());
429    if (dir == nullptr) {
430        FI_HILOGE("Failed to open directory \'%{public}s\':%{public}s", DEV_INPUT_PATH.c_str(), strerror(errno));
431        return RET_ERR;
432    }
433    struct dirent *dent;
434
435    while ((dent = readdir(dir)) != nullptr) {
436        const std::string devNode { dent->d_name };
437        const std::string devPath { DEV_INPUT_PATH + devNode };
438        struct stat statbuf;
439
440        if ((std::strcmp(dent->d_name, ".") == 0) || (std::strcmp(dent->d_name, "..") == 0)) {
441            continue;
442        }
443        if (stat(devPath.c_str(), &statbuf) != 0) {
444            continue;
445        }
446        if (!S_ISCHR(statbuf.st_mode)) {
447            continue;
448        }
449        auto vdev = std::make_shared<VirtualDevice>(devPath);
450        if (pred(vdev)) {
451            vDevs.push_back(vdev);
452        }
453    }
454    if (closedir(dir) != 0) {
455        FI_HILOGE("closedir error:%{public}s", strerror(errno));
456    }
457    return RET_OK;
458}
459
460std::shared_ptr<VirtualDevice> VirtualDeviceBuilder::Select(
461    std::vector<std::shared_ptr<VirtualDevice>> &vDevs, const char *name)
462{
463    CALL_DEBUG_ENTER;
464    if (vDevs.empty()) {
465        std::cout << "No " << name << "." << std::endl;
466        return nullptr;
467    }
468    auto vDev = vDevs.front();
469
470    if (vDevs.size() > 1) {
471        std::cout << "More than one " << name << " were found, please select one to clone:" << std::endl;
472        size_t index = 0;
473
474        for (const auto &v : vDevs) {
475            std::cout << "[" << index << "]\t" << v->GetName() << std::endl;
476            ++index;
477        }
478        std::cout << "[>=" << index << "]\tQuit" << std::endl;
479        std::cin >> index;
480        if (index >= vDevs.size()) {
481            std::cout << "Selected index is out of range, quit." << std::endl;
482            return nullptr;
483        }
484        vDev = vDevs[index];
485    }
486    return vDev;
487}
488
489void VirtualDeviceBuilder::CopyProperties(const std::string &name, std::shared_ptr<VirtualDevice> vDev)
490{
491    CHKPV(vDev);
492    CopyIdentity(name, vDev);
493    CopyAbsInfo(vDev);
494    CopyEvents(vDev);
495}
496
497void VirtualDeviceBuilder::CopyIdentity(const std::string &name, std::shared_ptr<VirtualDevice> vDev)
498{
499    CALL_DEBUG_ENTER;
500    CHKPV(vDev);
501    uinputDev_.id = vDev->GetInputId();
502    if (strcpy_s(uinputDev_.name, sizeof(uinputDev_.name), name.c_str()) != EOK) {
503        FI_HILOGE("Invalid device name:\'%{public}s\'", name.c_str());
504    }
505}
506
507void VirtualDeviceBuilder::CopyAbsInfo(std::shared_ptr<VirtualDevice> vDev)
508{
509    CALL_DEBUG_ENTER;
510    CHKPV(vDev);
511    for (size_t abs = ABS_X; abs < ABS_CNT; ++abs) {
512        struct uinput_abs_setup absSetup {
513            .code = static_cast<__u16>(abs),
514        };
515        if (!vDev->QueryAbsInfo(abs, absSetup.absinfo)) {
516            FI_HILOGE("Failed to get abs info for axis %{public}zu", abs);
517            continue;
518        }
519        if (absSetup.absinfo.value == 0 && absSetup.absinfo.minimum == 0 &&
520            absSetup.absinfo.maximum <= absSetup.absinfo.minimum && absSetup.absinfo.fuzz == 0 &&
521            absSetup.absinfo.flat == 0 && absSetup.absinfo.resolution == 0) {
522            continue;
523        }
524        absInit_.push_back(absSetup);
525        uinputDev_.absmin[abs] = absSetup.absinfo.minimum;
526        uinputDev_.absmax[abs] = absSetup.absinfo.maximum;
527        uinputDev_.absfuzz[abs] = absSetup.absinfo.fuzz;
528        uinputDev_.absflat[abs] = absSetup.absinfo.flat;
529    }
530}
531
532void VirtualDeviceBuilder::CopyEvents(std::shared_ptr<VirtualDevice> vDev)
533{
534    CALL_DEBUG_ENTER;
535    CHKPV(vDev);
536    for (uint32_t ev = EV_SYN; ev < EV_MAX; ++ev) {
537        if (vDev->SupportEventType(ev)) {
538            eventTypes_.push_back(ev);
539        }
540    }
541    for (uint32_t key = KEY_ESC; key < KEY_MAX; ++key) {
542        if (vDev->SupportKey(key)) {
543            keys_.push_back(key);
544        }
545    }
546    for (uint32_t abs = ABS_X; abs < ABS_MAX; ++abs) {
547        if (vDev->SupportAbs(abs)) {
548            abs_.push_back(abs);
549        }
550    }
551    for (uint32_t rel = REL_X; rel < REL_MAX; ++rel) {
552        if (vDev->SupportRel(rel)) {
553            relBits_.push_back(rel);
554        }
555    }
556    for (uint32_t msc = MSC_SERIAL; msc < MSC_MAX; ++msc) {
557        if (vDev->SupportMsc(msc)) {
558            miscellaneous_.push_back(msc);
559        }
560    }
561    for (uint32_t led = LED_NUML; led < LED_MAX; ++led) {
562        if (vDev->SupportLed(led)) {
563            leds_.push_back(led);
564        }
565    }
566    for (uint32_t rep = REP_DELAY; rep < REP_MAX; ++rep) {
567        if (vDev->SupportRep(rep)) {
568            repeats_.push_back(rep);
569        }
570    }
571    for (uint32_t prop = INPUT_PROP_POINTER; prop < INPUT_PROP_MAX; ++prop) {
572        if (vDev->SupportProperty(prop)) {
573            properties_.push_back(prop);
574        }
575    }
576}
577} // namespace DeviceStatus
578} // namespace Msdp
579} // namespace OHOS