Subversion Repositories public

Rev

Rev 51 | Rev 61 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
45 luk 1
 
2
/// inotify cron daemon user tables implementation
3
/**
4
 * \file usertable.cpp
5
 *
6
 * inotify cron system
7
 *
8
 * Copyright (C) 2006 Lukas Jelinek, <lukas@aiken.cz>
9
 *
10
 * This program is free software; you can use it, redistribute
11
 * it and/or modify it under the terms of the GNU General Public
12
 * License, version 2 (see LICENSE-GPL).
13
 *  
14
 */
15
 
16
 
17
#include <pwd.h>
18
#include <syslog.h>
19
#include <errno.h>
47 luk 20
#include <sys/wait.h>
45 luk 21
 
22
#include "usertable.h"
23
 
24
 
47 luk 25
PROC_LIST UserTable::s_procList;
26
 
27
 
28
void on_proc_done(InotifyWatch* pW)
29
{
30
  pW->SetEnabled(true);
31
}
32
 
33
 
45 luk 34
EventDispatcher::EventDispatcher(Inotify* pIn)
35
{
36
 m_pIn = pIn;
37
}
38
 
39
void EventDispatcher::DispatchEvent(InotifyEvent& rEvt)
40
{
41
  if (m_pIn == NULL)
42
    return;
43
 
44
  InotifyWatch* pW = rEvt.GetWatch();
45
  if (pW == NULL)
46
    return;
47
 
48
  UserTable* pT = FindTable(pW);
49
  if (pT == NULL)
50
    return;
51
 
52
  pT->OnEvent(rEvt);
53
}
54
 
55
void EventDispatcher::Register(InotifyWatch* pWatch, UserTable* pTab)
56
{
57
  if (pWatch != NULL && pTab != NULL)
58
    m_maps.insert(IWUT_MAP::value_type(pWatch, pTab));
59
}
60
 
61
void EventDispatcher::Unregister(InotifyWatch* pWatch)
62
{
63
  IWUT_MAP::iterator it = m_maps.find(pWatch);
64
  if (it == m_maps.end())
55 luk 65
    m_maps.erase(it);
45 luk 66
}
67
 
68
void EventDispatcher::UnregisterAll(UserTable* pTab)
69
{
70
  IWUT_MAP::iterator it = m_maps.begin();
71
  while (it != m_maps.end()) {
72
    if ((*it).second == pTab) {
73
      IWUT_MAP::iterator it2 = it;
74
      it++;
75
      m_maps.erase(it2);
76
    }
77
    else {
78
      it++;
79
    }
80
  }
81
}
82
 
83
UserTable* EventDispatcher::FindTable(InotifyWatch* pW)
84
{
85
  IWUT_MAP::iterator it = m_maps.find(pW);
86
  if (it == m_maps.end())
87
    return NULL;
88
 
89
  return (*it).second;
90
}
91
 
92
 
93
 
94
 
95
UserTable::UserTable(Inotify* pIn, EventDispatcher* pEd, const std::string& rUser)
96
: m_user(rUser)
97
{
98
  m_pIn = pIn;
99
  m_pEd = pEd;
100
}
101
 
102
UserTable::~UserTable()
103
{
104
  Dispose();
105
}
106
 
107
void UserTable::Load()
108
{
109
  m_tab.Load(InCronTab::GetUserTablePath(m_user));
110
 
111
  int cnt = m_tab.GetCount();
112
  for (int i=0; i<cnt; i++) {
113
    InCronTabEntry& rE = m_tab.GetEntry(i);
114
    InotifyWatch* pW = new InotifyWatch(rE.GetPath(), rE.GetMask());
47 luk 115
    try {
116
      m_pIn->Add(pW);
117
      m_pEd->Register(pW, this);
118
      m_map.insert(IWCE_MAP::value_type(pW, &rE));
119
    } catch (InotifyException e) {
120
      syslog(LOG_ERR, "cannot create watch for user %s", m_user.c_str());
121
      delete pW;
122
    }
45 luk 123
  }
124
}
125
 
126
void UserTable::Dispose()
127
{
128
  IWCE_MAP::iterator it = m_map.begin();
129
  while (it != m_map.end()) {
130
    InotifyWatch* pW = (*it).first;
131
    m_pEd->Unregister(pW);
132
    m_pIn->Remove(pW);
133
    delete pW;
134
    it++;
135
  }
136
 
137
  m_map.clear();
138
}
139
 
140
void UserTable::OnEvent(InotifyEvent& rEvt)
141
{
142
  InotifyWatch* pW = rEvt.GetWatch();
143
  InCronTabEntry* pE = FindEntry(pW);
144
 
145
  if (pE == NULL)
146
    return;
147
 
148
  std::string cmd;
149
  const std::string& cs = pE->GetCmd();
150
  size_t pos = 0;
151
  size_t oldpos = 0;
152
  size_t len = cs.length();
153
  while ((pos = cs.find('$', oldpos)) != std::string::npos) {
154
    if (pos < len - 1) {
155
      size_t px = pos + 1;
156
      if (cs[px] == '$') {
157
        cmd.append(cs.substr(oldpos, pos-oldpos+1));
158
        oldpos = pos + 2;
159
      }
160
      else {
161
        cmd.append(cs.substr(oldpos, pos-oldpos));
55 luk 162
        if (cs[px] == '@') {          // base path
163
          cmd.append(pW->GetPath());
164
          oldpos = pos + 2;
165
        }
166
        else if (cs[px] == '#') {     // file name
167
          cmd.append(rEvt.GetName());
168
          oldpos = pos + 2;
169
        }
170
        else if (cs[px] == '%') {     // mask symbols
171
          std::string s;
172
          rEvt.DumpTypes(s);
173
          cmd.append(s);
174
          oldpos = pos + 2;
175
        }
176
        else if (cs[px] == '&') {     // numeric mask
177
          char* s;
178
          asprintf(&s, "%u", (unsigned) rEvt.GetMask());
179
          cmd.append(s);
180
          free(s);
181
          oldpos = pos + 2;
182
        }
183
        else {
184
          oldpos = pos + 1;
185
        }
45 luk 186
      }
187
    }
188
    else {
189
      cmd.append(cs.substr(oldpos, pos-oldpos));
190
      oldpos = pos + 1;
191
    }
192
  }    
193
  cmd.append(cs.substr(oldpos));
194
 
195
  int argc;
196
  char** argv;
197
  if (!PrepareArgs(cmd, argc, argv)) {
198
    syslog(LOG_ERR, "cannot prepare command arguments");
199
    return;
200
  }
201
 
202
  syslog(LOG_INFO, "(%s) CMD (%s)", m_user.c_str(), cmd.c_str());
203
 
47 luk 204
  if (pE->IsNoLoop())
205
    pW->SetEnabled(false);
206
 
207
  ProcData_t pd;
208
  pd.pid = fork();
209
  if (pd.pid == 0) {
45 luk 210
 
211
    struct passwd* pwd = getpwnam(m_user.c_str());
51 luk 212
    if (    pwd == NULL                 // user not found
213
        ||  setuid(pwd->pw_uid) != 0    // setting UID failed
214
        ||  execvp(argv[0], argv) != 0) // exec failed
215
    {
45 luk 216
      _exit(1);
217
    }
218
  }
47 luk 219
  else if (pd.pid > 0) {
220
    if (pE->IsNoLoop()) {
221
      pd.onDone = on_proc_done;
222
      pd.pWatch = pW;
223
    }
224
    else {
225
      pd.onDone = NULL;
226
      pd.pWatch = NULL;
227
    }
45 luk 228
 
47 luk 229
    s_procList.push_back(pd);
45 luk 230
  }
231
  else {
47 luk 232
    if (pE->IsNoLoop())
233
      pW->SetEnabled(true);
234
 
45 luk 235
    syslog(LOG_ERR, "cannot fork process: %s", strerror(errno));
236
  }
51 luk 237
 
238
  CleanupArgs(argc, argv);
45 luk 239
}
240
 
241
InCronTabEntry* UserTable::FindEntry(InotifyWatch* pWatch)
242
{
243
  IWCE_MAP::iterator it = m_map.find(pWatch);
244
  if (it == m_map.end())
245
    return NULL;
246
 
247
  return (*it).second;
248
}
249
 
250
bool UserTable::PrepareArgs(const std::string& rCmd, int& argc, char**& argv)
251
{
252
  if (rCmd.empty())
253
    return false;
254
 
55 luk 255
  StringTokenizer tok(rCmd, ' ', '\\');
45 luk 256
  std::deque<std::string> args;
257
  while (tok.HasMoreTokens()) {
258
    args.push_back(tok.GetNextToken());
259
  }
260
 
261
  if (args.empty())
262
    return false;
263
 
264
  argc = (int) args.size();
265
  argv = new char*[argc+1];
266
  argv[argc] = NULL;
267
 
268
  for (int i=0; i<argc; i++) {
269
    const std::string& s = args[i];
270
    size_t len = s.length();
271
    argv[i] = new char[len+1];
272
    strcpy(argv[i], s.c_str());
273
  }
274
 
275
  return true;
276
}
277
 
51 luk 278
void UserTable::CleanupArgs(int argc, char** argv)
279
{
280
  for (int i=0; i<argc; i++) {
281
    delete[] argv[i];
282
  }
283
 
284
  delete[] argv;
285
}
286
 
47 luk 287
void UserTable::FinishDone()
288
{
289
  PROC_LIST::iterator it = s_procList.begin();
290
  while (it != s_procList.end()) {
291
    ProcData_t& pd = *it;
292
    int status = 0;
293
    int res = waitpid(pd.pid, &status, WNOHANG);
294
    if (res == pd.pid && (WIFEXITED(status) || WIFSIGNALED(status))) {
295
      if (pd.onDone != NULL)
296
        (*pd.onDone)(pd.pWatch);
297
      it = s_procList.erase(it);
298
    }
299
    else {
300
      it++;
301
    }
302
  }  
303
}
304
 
305