Subversion Repositories public

Rev

Rev 55 | Rev 63 | 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
61 luk 214
        ||  setgid(pwd->pw_gid) != 0    // setting GID failed
51 luk 215
        ||  execvp(argv[0], argv) != 0) // exec failed
216
    {
45 luk 217
      _exit(1);
218
    }
219
  }
47 luk 220
  else if (pd.pid > 0) {
221
    if (pE->IsNoLoop()) {
222
      pd.onDone = on_proc_done;
223
      pd.pWatch = pW;
224
    }
225
    else {
226
      pd.onDone = NULL;
227
      pd.pWatch = NULL;
228
    }
45 luk 229
 
47 luk 230
    s_procList.push_back(pd);
45 luk 231
  }
232
  else {
47 luk 233
    if (pE->IsNoLoop())
234
      pW->SetEnabled(true);
235
 
45 luk 236
    syslog(LOG_ERR, "cannot fork process: %s", strerror(errno));
237
  }
51 luk 238
 
239
  CleanupArgs(argc, argv);
45 luk 240
}
241
 
242
InCronTabEntry* UserTable::FindEntry(InotifyWatch* pWatch)
243
{
244
  IWCE_MAP::iterator it = m_map.find(pWatch);
245
  if (it == m_map.end())
246
    return NULL;
247
 
248
  return (*it).second;
249
}
250
 
251
bool UserTable::PrepareArgs(const std::string& rCmd, int& argc, char**& argv)
252
{
253
  if (rCmd.empty())
254
    return false;
255
 
55 luk 256
  StringTokenizer tok(rCmd, ' ', '\\');
45 luk 257
  std::deque<std::string> args;
258
  while (tok.HasMoreTokens()) {
259
    args.push_back(tok.GetNextToken());
260
  }
261
 
262
  if (args.empty())
263
    return false;
264
 
265
  argc = (int) args.size();
266
  argv = new char*[argc+1];
267
  argv[argc] = NULL;
268
 
269
  for (int i=0; i<argc; i++) {
270
    const std::string& s = args[i];
271
    size_t len = s.length();
272
    argv[i] = new char[len+1];
273
    strcpy(argv[i], s.c_str());
274
  }
275
 
276
  return true;
277
}
278
 
51 luk 279
void UserTable::CleanupArgs(int argc, char** argv)
280
{
281
  for (int i=0; i<argc; i++) {
282
    delete[] argv[i];
283
  }
284
 
285
  delete[] argv;
286
}
287
 
47 luk 288
void UserTable::FinishDone()
289
{
290
  PROC_LIST::iterator it = s_procList.begin();
291
  while (it != s_procList.end()) {
292
    ProcData_t& pd = *it;
293
    int status = 0;
294
    int res = waitpid(pd.pid, &status, WNOHANG);
295
    if (res == pd.pid && (WIFEXITED(status) || WIFSIGNALED(status))) {
296
      if (pd.onDone != NULL)
297
        (*pd.onDone)(pd.pWatch);
298
      it = s_procList.erase(it);
299
    }
300
    else {
301
      it++;
302
    }
303
  }  
304
}
305
 
306