2
* Copyright 2015-2016 The Regents of the University of California
5
* This file is part of Spoofer.
7
* Spoofer is free software: you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License as published by
9
* the Free Software Foundation, either version 3 of the License, or
10
* (at your option) any later version.
12
* Spoofer is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
17
* You should have received a copy of the GNU General Public License
18
* along with Spoofer. If not, see <http://www.gnu.org/licenses/>.
25
#include <QFileSystemWatcher>
26
#include "../../config.h"
30
static const char cvsid[] ATR_USED = "$Id: appmac.cpp,v 1.2.2.1 2016/10/13 21:19:01 kkeys Exp $";
32
bool AppMac::prestart(int &exitCode)
34
if (isLaunchdService()) {
36
qDebug() << "Scheduler: ignoring detatch option";
40
// Try to detect if our own executable is deleted (i.e. user is trying
41
// to uninstall by dragging the Spoofer.app bundle to the trash).
42
QString bundleDir(appDir);
43
bundleDir.remove(QSL("/Contents/MacOS")); // -> "/Applications/Spoofer.app"
44
QString bundleParent(bundleDir % QSL("/.."));
45
exeWatcher = new QFileSystemWatcher();
46
QStringList list = (QStringList() <<
47
appFile << bundleDir << bundleParent);
48
qDebug() << "watching" << list;
49
QStringList failed = exeWatcher->addPaths(list);
50
if (!failed.isEmpty()) {
51
qDebug() << "failed to watch" << failed;
53
if (failed.size() == list.size()) {
56
connect(exeWatcher, &QFileSystemWatcher::fileChanged,
57
this, &AppMac::executableChanged);
58
connect(exeWatcher, &QFileSystemWatcher::directoryChanged,
59
this, &AppMac::executableChanged);
63
return AppUnix::prestart(exitCode);
66
#define SERVICENAME ORG_DOMAIN_REVERSED ".spoofer-scheduler"
67
#define LAUNCHERCFG "/Library/LaunchDaemons/" SERVICENAME ".plist"
69
// The launchd API is undocumented, but see:
70
// /usr/include/launch.h
71
// http://opensource.apple.com//source/samba/samba-187/tools/prefsync/lib/launchctl.cpp
72
launch_data_t AppMac::launchdRequest(const char *key)
74
launch_data_t req = 0;
75
launch_data_t resp = 0;
77
if (!(req = launch_data_alloc(LAUNCH_DATA_DICTIONARY))) {
78
sperr << "launch_data_alloc failed" << endl;
81
launch_data_dict_insert(req, launch_data_new_string(SERVICENAME), key);
82
resp = launch_msg(req);
84
sperr << "launchd " << key << " " << SERVICENAME << ": error: " <<
85
strerror(errno) << endl;
86
launch_data_free(req);
90
bool AppMac::isLaunchdService()
93
launch_data_t resp = 0;
94
launch_data_t data = 0;
95
pid_t servicePid = 0, myPid = getpid();
97
// Verify that this process is the spoofer-scheduler service in launchd.
98
if (!(resp = launchdRequest(LAUNCH_KEY_GETJOB)))
100
if ((data = launch_data_dict_lookup(resp, LAUNCH_JOBKEY_PID)))
101
servicePid = (pid_t)launch_data_get_integer(data);
102
if (!(result = (servicePid == myPid))) {
103
qDebug() << "service pid" << servicePid << "!= scheduler pid" << myPid;
105
qDebug() << "confirmed: running with pid" << myPid <<
106
"under launchd as" << SERVICENAME;
108
launch_data_free(resp);
112
void AppMac::removeLaunchdService()
115
launch_data_type_t type;
117
// Remove the service.
118
if (!(resp = launchdRequest(LAUNCH_KEY_REMOVEJOB)))
120
if ((type = launch_data_get_type(resp)) != LAUNCH_DATA_ERRNO) {
121
sperr << "unexpected launchd response type " << type << endl;
123
errno = launch_data_get_errno(resp);
125
sperr << "launchd " << LAUNCH_KEY_REMOVEJOB << " " << SERVICENAME <<
126
": error: " << strerror(errno) << endl;
128
sperr << "launchd remove " << SERVICENAME << ": success" << endl;
131
launch_data_free(resp);
134
void AppMac::executableChanged(const QString &path)
136
qDebug() << "change detected in" << path;
137
if (QFile(appFile).exists())
139
sperr << "appFile " << appFile << " was deleted" << endl;
141
if (!isLaunchdService())
144
removeLaunchdService();
146
// delete our launchd config file
147
if (unlink(LAUNCHERCFG) == 0)
148
sperr << "Deleted " << LAUNCHERCFG << endl;
150
sperr << "Error deleting " << LAUNCHERCFG << ": " << strerror(errno) <<
156
void AppMac::shutdown()
158
// If we're running under launchd, remove ourselves so we're not
159
// automatically restarted as soon as we exit. But we don't delete the
160
// config file, so we can be reloaded later (e.g. at next boot).
161
if (isLaunchdService())
162
removeLaunchdService();