~unity-api-team/unity-scopes-api/child-scopes-option

« back to all changes in this revision

Viewing changes to scoperegistry/SignalThread.cpp

  • Committer: Michi Henning
  • Date: 2013-11-19 02:51:46 UTC
  • mto: This revision was merged to the branch mainline in revision 62.
  • Revision ID: michi.henning@canonical.com-20131119025146-kglcalpphl4ozzk7
Fixed scoperegistry to use new scoperunner and to figure out which scopes to run from config files.
Still to do:
- deal with overrides and OEM scopes, particularly the grouping aspect.
- SignalThread needs to invoke a callback for clean shut-down on receipt of SIGINT.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2013 Canonical Ltd
 
3
 *
 
4
 * This program is free software: you can redistribute it and/or modify
 
5
 * it under the terms of the GNU General Public License version 3 as
 
6
 * published by the Free Software Foundation.
 
7
 *
 
8
 * This program is distributed in the hope that it will be useful,
 
9
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
 * GNU General Public License for more details.
 
12
 *
 
13
 * You should have received a copy of the GNU General Public License
 
14
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
15
 *
 
16
 * Authored by: Michi Henning <michi.henning@canonical.com>
 
17
 */
 
18
 
 
19
#include <SignalThread.h>
 
20
 
 
21
#include <unity/UnityExceptions.h>
 
22
 
 
23
#include <cassert>
 
24
#include <iostream>
 
25
 
 
26
#include <signal.h>
 
27
#include <string.h>
 
28
#include <sys/wait.h>
 
29
#include <unistd.h>
 
30
 
 
31
using namespace std;
 
32
using namespace unity;
 
33
 
 
34
namespace scoperegistry
 
35
{
 
36
 
 
37
SignalThread::SignalThread() :
 
38
    done_(false)
 
39
{
 
40
    // Block signals in the caller, so they are delivered to the thread we are about to create.
 
41
    sigemptyset(&sigs_);
 
42
    sigaddset(&sigs_, SIGCHLD);
 
43
    sigaddset(&sigs_, SIGINT);
 
44
    sigaddset(&sigs_, SIGHUP);
 
45
    sigaddset(&sigs_, SIGTERM);
 
46
    sigaddset(&sigs_, SIGUSR1);
 
47
    int err = pthread_sigmask(SIG_BLOCK, &sigs_, nullptr);
 
48
    if (err != 0)
 
49
    {
 
50
        throw SyscallException("pthread_sigmask failed", err);
 
51
    }
 
52
 
 
53
    // Make ourselves a process group leader.
 
54
    setpgid(0, 0);
 
55
 
 
56
    // Run a signal handling thread that waits for any of the above signals.
 
57
    handler_thread_ = thread([this]{ this->wait_for_sigs(); });
 
58
}
 
59
 
 
60
SignalThread::~SignalThread() noexcept
 
61
{
 
62
    {
 
63
        lock_guard<mutex> lock(mutex_);
 
64
        if (!done_)
 
65
        {
 
66
            kill(getpid(), SIGUSR1);
 
67
        }
 
68
    }
 
69
    handler_thread_.join();
 
70
}
 
71
 
 
72
// Add the pid and supplied command line to the table of children.
 
73
 
 
74
void SignalThread::add_child(pid_t pid, char const* argv[])
 
75
{
 
76
    lock_guard<mutex> lock(mutex_);
 
77
    if (done_)
 
78
    {
 
79
        return; // We've been told to go away already
 
80
    }
 
81
    string args;
 
82
    for (int i = 0; argv[i] != nullptr; ++i)
 
83
    {
 
84
        if (i != 0)
 
85
        {
 
86
            args.append(" ");
 
87
        }
 
88
        args.append(argv[i]);
 
89
    }
 
90
    children_[pid] = args;
 
91
}
 
92
 
 
93
// Unblock the signals we are handling. This allows the child to reset the signals
 
94
// to their defaults before calling exec().
 
95
 
 
96
void SignalThread::reset_sigs()
 
97
{
 
98
    int err = pthread_sigmask(SIG_UNBLOCK, &sigs_, nullptr);
 
99
    if (err != 0)
 
100
    {
 
101
        throw SyscallException("pthread_sigmask failed", err);
 
102
    }
 
103
}
 
104
 
 
105
// Wait for signals. For SIGCHLD, we check exit status and report if a child terminated abnormally.
 
106
// The normal termination signals (SIGINT, SIGHUP, and SIGTERM) are sent to the children.
 
107
// SIGUSR1 causes the thread to return without touching any of its children.
 
108
 
 
109
void SignalThread::wait_for_sigs()
 
110
{
 
111
    int signo;
 
112
    for (;;)
 
113
    {
 
114
        int err = sigwait(&sigs_, &signo);
 
115
        if (err != 0)
 
116
        {
 
117
            cerr << "scoperegistry: sigwait failed: " << strerror(err) << endl;
 
118
            _exit(1);
 
119
        }
 
120
        switch (signo)
 
121
        {
 
122
            case SIGCHLD:
 
123
            {
 
124
                for (;;)
 
125
                {
 
126
                    // Multiple children exiting can result in only a single SIGCHLD being received,
 
127
                    // so we loop until there are no more exited children.
 
128
                    int status;
 
129
                    pid_t pid = waitpid(-1, &status, WNOHANG);
 
130
                    if (pid == -1)
 
131
                    {
 
132
                        if (errno != ECHILD)
 
133
                        {
 
134
                            cerr << "scoperegistry: wait() failed: " << strerror(errno) << endl;
 
135
                            _exit(1);
 
136
                        }
 
137
                        continue; // Ignore stray SIGCHLD signals
 
138
                    }
 
139
                    if (pid == 0)
 
140
                    {
 
141
                        break;  // No more exited children remain.
 
142
                    }
 
143
 
 
144
                    lock_guard<mutex> lock(mutex_);
 
145
                    auto it = children_.find(pid);
 
146
                    if (it == children_.end())
 
147
                    {
 
148
                        cerr << "scoperegistry: ignoring unexpected child termination, pid = " << pid << endl;
 
149
                        continue;
 
150
                    }
 
151
                    if (WIFEXITED(status))
 
152
                    {
 
153
                        if (WEXITSTATUS(status) != 0)
 
154
                        {
 
155
                            cerr << "scoperegistry: process " << pid << " exited with status " << WEXITSTATUS(status)
 
156
                                 << ", command line: " << it->second << endl;
 
157
                        }
 
158
                    }
 
159
                    else
 
160
                    {
 
161
                        cerr << "scoperegistry: process " << pid << " terminated by signal " << WTERMSIG(status)
 
162
                             << ", command line: " << it->second << endl;
 
163
                    }
 
164
                    children_.erase(it);
 
165
                }
 
166
                break;
 
167
            }
 
168
            case SIGINT:
 
169
            case SIGHUP:
 
170
            case SIGTERM:
 
171
            {
 
172
                // Send the signal to all the children.
 
173
                lock_guard<mutex> lock(mutex_);
 
174
                for (auto pair : children_)
 
175
                {
 
176
                    kill(pair.first, signo);
 
177
                }
 
178
                done_ = true;
 
179
                // TODO: for now, until the callback is added, just exit
 
180
                _exit(signo);
 
181
                break;
 
182
            }
 
183
            case SIGUSR1:
 
184
            {
 
185
                cerr << "signal thread: Got SIGUSR1" << endl;
 
186
                // TODO: should invoke a callback here that then can shut down the run time.
 
187
                return; // Go away without doing anything to the children.
 
188
            }
 
189
            default:
 
190
            {
 
191
                assert(false);
 
192
            }
 
193
        }
 
194
    }
 
195
}
 
196
 
 
197
} // namespace scoperegistry