Subversion Repositories public

Rev

Rev 63 | Rev 67 | 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
 *
63 luk 8
 * Copyright (C) 2006, 2007 Lukas Jelinek, <lukas@aiken.cz>
45 luk 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>
65 luk 21
#include <unistd.h>
22
#include <grp.h>
23
#include <sys/stat.h>
45 luk 24
 
25
#include "usertable.h"
26
 
65 luk 27
#ifdef IN_DONT_FOLLOW
28
#define NO_FOLLOW(mask) InotifyEvent::IsType(mask, IN_DONT_FOLLOW)
29
#else // IN_DONT_FOLLOW
30
#define NO_FOLLOW(mask) (false)
31
#endif // IN_DONT_FOLLOW
45 luk 32
 
65 luk 33
 
47 luk 34
PROC_LIST UserTable::s_procList;
35
 
36
 
37
void on_proc_done(InotifyWatch* pW)
38
{
39
  pW->SetEnabled(true);
40
}
41
 
42
 
45 luk 43
EventDispatcher::EventDispatcher(Inotify* pIn)
44
{
45
 m_pIn = pIn;
46
}
47
 
48
void EventDispatcher::DispatchEvent(InotifyEvent& rEvt)
49
{
50
  if (m_pIn == NULL)
51
    return;
52
 
53
  InotifyWatch* pW = rEvt.GetWatch();
54
  if (pW == NULL)
55
    return;
56
 
57
  UserTable* pT = FindTable(pW);
58
  if (pT == NULL)
59
    return;
60
 
61
  pT->OnEvent(rEvt);
62
}
63
 
64
void EventDispatcher::Register(InotifyWatch* pWatch, UserTable* pTab)
65
{
66
  if (pWatch != NULL && pTab != NULL)
67
    m_maps.insert(IWUT_MAP::value_type(pWatch, pTab));
68
}
69
 
70
void EventDispatcher::Unregister(InotifyWatch* pWatch)
71
{
72
  IWUT_MAP::iterator it = m_maps.find(pWatch);
73
  if (it == m_maps.end())
55 luk 74
    m_maps.erase(it);
45 luk 75
}
76
 
77
void EventDispatcher::UnregisterAll(UserTable* pTab)
78
{
79
  IWUT_MAP::iterator it = m_maps.begin();
80
  while (it != m_maps.end()) {
81
    if ((*it).second == pTab) {
82
      IWUT_MAP::iterator it2 = it;
83
      it++;
84
      m_maps.erase(it2);
85
    }
86
    else {
87
      it++;
88
    }
89
  }
90
}
91
 
92
UserTable* EventDispatcher::FindTable(InotifyWatch* pW)
93
{
94
  IWUT_MAP::iterator it = m_maps.find(pW);
95
  if (it == m_maps.end())
96
    return NULL;
97
 
98
  return (*it).second;
99
}
100
 
101
 
102
 
103
 
104
UserTable::UserTable(Inotify* pIn, EventDispatcher* pEd, const std::string& rUser)
105
: m_user(rUser)
106
{
107
  m_pIn = pIn;
108
  m_pEd = pEd;
109
}
110
 
111
UserTable::~UserTable()
112
{
113
  Dispose();
114
}
115
 
116
void UserTable::Load()
117
{
118
  m_tab.Load(InCronTab::GetUserTablePath(m_user));
119
 
120
  int cnt = m_tab.GetCount();
121
  for (int i=0; i<cnt; i++) {
122
    InCronTabEntry& rE = m_tab.GetEntry(i);
123
    InotifyWatch* pW = new InotifyWatch(rE.GetPath(), rE.GetMask());
65 luk 124
 
125
    // warning only - permissions may change later
126
    if (!MayAccess(rE.GetPath(), NO_FOLLOW(rE.GetMask())))
127
      syslog(LOG_WARNING, "access denied on %s - events will be discarded silently", rE.GetPath().c_str());
128
 
47 luk 129
    try {
130
      m_pIn->Add(pW);
131
      m_pEd->Register(pW, this);
132
      m_map.insert(IWCE_MAP::value_type(pW, &rE));
133
    } catch (InotifyException e) {
134
      syslog(LOG_ERR, "cannot create watch for user %s", m_user.c_str());
135
      delete pW;
136
    }
45 luk 137
  }
138
}
139
 
140
void UserTable::Dispose()
141
{
142
  IWCE_MAP::iterator it = m_map.begin();
143
  while (it != m_map.end()) {
144
    InotifyWatch* pW = (*it).first;
145
    m_pEd->Unregister(pW);
146
    m_pIn->Remove(pW);
147
    delete pW;
148
    it++;
149
  }
150
 
151
  m_map.clear();
152
}
153
 
154
void UserTable::OnEvent(InotifyEvent& rEvt)
155
{
156
  InotifyWatch* pW = rEvt.GetWatch();
157
  InCronTabEntry* pE = FindEntry(pW);
158
 
65 luk 159
  // no entry found - this shouldn't occur
45 luk 160
  if (pE == NULL)
161
    return;
162
 
65 luk 163
  // discard event if user has no access rights to watch path
164
  if (!MayAccess(pW->GetPath(), NO_FOLLOW(rEvt.GetMask())))
165
    return;
166
 
45 luk 167
  std::string cmd;
168
  const std::string& cs = pE->GetCmd();
169
  size_t pos = 0;
170
  size_t oldpos = 0;
171
  size_t len = cs.length();
172
  while ((pos = cs.find('$', oldpos)) != std::string::npos) {
173
    if (pos < len - 1) {
174
      size_t px = pos + 1;
175
      if (cs[px] == '$') {
176
        cmd.append(cs.substr(oldpos, pos-oldpos+1));
177
        oldpos = pos + 2;
178
      }
179
      else {
180
        cmd.append(cs.substr(oldpos, pos-oldpos));
55 luk 181
        if (cs[px] == '@') {          // base path
182
          cmd.append(pW->GetPath());
183
          oldpos = pos + 2;
184
        }
185
        else if (cs[px] == '#') {     // file name
186
          cmd.append(rEvt.GetName());
187
          oldpos = pos + 2;
188
        }
189
        else if (cs[px] == '%') {     // mask symbols
190
          std::string s;
191
          rEvt.DumpTypes(s);
192
          cmd.append(s);
193
          oldpos = pos + 2;
194
        }
195
        else if (cs[px] == '&') {     // numeric mask
196
          char* s;
197
          asprintf(&s, "%u", (unsigned) rEvt.GetMask());
198
          cmd.append(s);
199
          free(s);
200
          oldpos = pos + 2;
201
        }
202
        else {
203
          oldpos = pos + 1;
204
        }
45 luk 205
      }
206
    }
207
    else {
208
      cmd.append(cs.substr(oldpos, pos-oldpos));
209
      oldpos = pos + 1;
210
    }
211
  }    
212
  cmd.append(cs.substr(oldpos));
213
 
214
  int argc;
215
  char** argv;
216
  if (!PrepareArgs(cmd, argc, argv)) {
217
    syslog(LOG_ERR, "cannot prepare command arguments");
218
    return;
219
  }
220
 
221
  syslog(LOG_INFO, "(%s) CMD (%s)", m_user.c_str(), cmd.c_str());
222
 
47 luk 223
  if (pE->IsNoLoop())
224
    pW->SetEnabled(false);
225
 
226
  ProcData_t pd;
227
  pd.pid = fork();
228
  if (pd.pid == 0) {
45 luk 229
 
230
    struct passwd* pwd = getpwnam(m_user.c_str());
51 luk 231
    if (    pwd == NULL                 // user not found
63 luk 232
        ||  setgid(pwd->pw_gid) != 0    // setting GID failed
51 luk 233
        ||  setuid(pwd->pw_uid) != 0    // setting UID failed
234
        ||  execvp(argv[0], argv) != 0) // exec failed
235
    {
63 luk 236
      syslog(LOG_ERR, "cannot exec process: %s", strerror(errno));
45 luk 237
      _exit(1);
238
    }
239
  }
47 luk 240
  else if (pd.pid > 0) {
241
    if (pE->IsNoLoop()) {
242
      pd.onDone = on_proc_done;
243
      pd.pWatch = pW;
244
    }
245
    else {
246
      pd.onDone = NULL;
247
      pd.pWatch = NULL;
248
    }
45 luk 249
 
47 luk 250
    s_procList.push_back(pd);
45 luk 251
  }
252
  else {
47 luk 253
    if (pE->IsNoLoop())
254
      pW->SetEnabled(true);
255
 
45 luk 256
    syslog(LOG_ERR, "cannot fork process: %s", strerror(errno));
257
  }
51 luk 258
 
259
  CleanupArgs(argc, argv);
45 luk 260
}
261
 
262
InCronTabEntry* UserTable::FindEntry(InotifyWatch* pWatch)
263
{
264
  IWCE_MAP::iterator it = m_map.find(pWatch);
265
  if (it == m_map.end())
266
    return NULL;
267
 
268
  return (*it).second;
269
}
270
 
271
bool UserTable::PrepareArgs(const std::string& rCmd, int& argc, char**& argv)
272
{
273
  if (rCmd.empty())
274
    return false;
275
 
55 luk 276
  StringTokenizer tok(rCmd, ' ', '\\');
45 luk 277
  std::deque<std::string> args;
278
  while (tok.HasMoreTokens()) {
279
    args.push_back(tok.GetNextToken());
280
  }
281
 
282
  if (args.empty())
283
    return false;
284
 
285
  argc = (int) args.size();
286
  argv = new char*[argc+1];
287
  argv[argc] = NULL;
288
 
289
  for (int i=0; i<argc; i++) {
290
    const std::string& s = args[i];
291
    size_t len = s.length();
292
    argv[i] = new char[len+1];
293
    strcpy(argv[i], s.c_str());
294
  }
295
 
296
  return true;
297
}
298
 
51 luk 299
void UserTable::CleanupArgs(int argc, char** argv)
300
{
301
  for (int i=0; i<argc; i++) {
302
    delete[] argv[i];
303
  }
304
 
305
  delete[] argv;
306
}
307
 
47 luk 308
void UserTable::FinishDone()
309
{
310
  PROC_LIST::iterator it = s_procList.begin();
311
  while (it != s_procList.end()) {
312
    ProcData_t& pd = *it;
313
    int status = 0;
314
    int res = waitpid(pd.pid, &status, WNOHANG);
315
    if (res == pd.pid && (WIFEXITED(status) || WIFSIGNALED(status))) {
316
      if (pd.onDone != NULL)
317
        (*pd.onDone)(pd.pWatch);
318
      it = s_procList.erase(it);
319
    }
320
    else {
321
      it++;
322
    }
323
  }  
324
}
325
 
65 luk 326
bool UserTable::MayAccess(const std::string& rPath, bool fNoFollow) const
327
{
328
  // first, retrieve file permissions
329
  struct stat st;
330
  int res = fNoFollow
331
      ? lstat(rPath.c_str(), &st) // don't follow symlink
332
      : stat(rPath.c_str(), &st);
333
  if (res != 0)
334
    return false; // retrieving permissions failed
335
 
336
  // file accessible to everyone
337
  if (st.st_mode & S_IRWXO)
338
    return true;
339
 
340
  // retrieve user data
341
  struct passwd* pwd = getpwnam(m_user.c_str());
342
 
343
  // file accesible to group
344
  if (st.st_mode & S_IRWXG) {
345
 
346
    // user's primary group
347
    if (pwd != NULL && pwd->pw_gid == st.st_gid)
348
        return true;
349
 
350
    // now check group database
351
    struct group *gr = getgrgid(st.st_gid);
352
    if (gr != NULL) {
353
      int pos = 0;
354
      const char* un;
355
      while ((un = gr->gr_mem[pos]) != NULL) {
356
        if (strcmp(un, m_user.c_str()) == 0)
357
          return true;
358
        pos++;
359
      }
360
    }
361
  }
362
 
363
  // file accessible to owner
364
  if (st.st_mode & S_IRWXU) {  
365
    if (pwd != NULL && pwd->pw_uid == st.st_uid)
366
      return true;
367
  }
368
 
369
  return false; // no access right found
370
}
47 luk 371
 
65 luk 372
 
373