--- a/Mailman/Archiver/Archiver.py 2009-01-11 10:49:38.000000000 -0700 +++ b/Mailman/Archiver/Archiver.py 2010-01-14 11:59:33.000000000 -0700 @@ -93,7 +93,7 @@ omask = os.umask(0) try: try: - os.mkdir(self.archive_dir()+'.mbox', 02775) + os.mkdir(self.archive_dir()+'.mbox', 0775) except OSError, e: if e.errno <> errno.EEXIST: raise # We also create an empty pipermail archive directory into @@ -101,7 +101,7 @@ # that lists that have not yet received a posting have # /something/ as their index.html, and don't just get a 404. try: - os.mkdir(self.archive_dir(), 02775) + os.mkdir(self.archive_dir(), 0775) except OSError, e: if e.errno <> errno.EEXIST: raise # See if there's an index.html file there already and if not, --- a/Mailman/Archiver/HyperArch.py 2009-01-11 10:49:38.000000000 -0700 +++ b/Mailman/Archiver/HyperArch.py 2010-01-14 11:59:33.000000000 -0700 @@ -623,7 +623,7 @@ __super_add_article = pipermail.T.add_article # some defaults - DIRMODE = 02775 + DIRMODE = 0775 FILEMODE = 0660 VERBOSE = 0 --- a/Mailman/Archiver/HyperDatabase.py 2009-01-11 10:49:38.000000000 -0700 +++ b/Mailman/Archiver/HyperDatabase.py 2010-01-14 11:59:33.000000000 -0700 @@ -242,7 +242,7 @@ omask = os.umask(0) try: try: - os.mkdir(arcdir, 02770) + os.mkdir(arcdir, 0770) except OSError, e: if e.errno <> errno.EEXIST: raise finally: --- a/Mailman/Archiver/pipermail.py 2009-01-11 10:49:38.000000000 -0700 +++ b/Mailman/Archiver/pipermail.py 2010-01-14 11:59:33.000000000 -0700 @@ -775,7 +775,7 @@ omask = os.umask(0) try: try: - os.mkdir(arcdir, 02775) + os.mkdir(arcdir, 0775) except OSError: # BAW: Hmm... pass --- a/Mailman/Cgi/edithtml.py 2009-01-11 10:49:38.000000000 -0700 +++ b/Mailman/Cgi/edithtml.py 2010-01-14 11:59:33.000000000 -0700 @@ -178,7 +178,7 @@ omask = os.umask(0) try: try: - os.mkdir(langdir, 02775) + os.mkdir(langdir, 0775) except OSError, e: if e.errno <> errno.EEXIST: raise finally: --- a/Mailman/Handlers/Scrubber.py 2009-01-11 10:49:38.000000000 -0700 +++ b/Mailman/Handlers/Scrubber.py 2010-01-14 11:59:33.000000000 -0700 @@ -417,11 +417,11 @@ def makedirs(dir): # Create all the directories to store this attachment in try: - os.makedirs(dir, 02775) + os.makedirs(dir, 0775) # Unfortunately, FreeBSD seems to be broken in that it doesn't honor # the mode arg of mkdir(). def twiddle(arg, dirname, names): - os.chmod(dirname, 02775) + os.chmod(dirname, 0775) os.path.walk(dir, twiddle, None) except OSError, e: if e.errno <> errno.EEXIST: raise --- a/Mailman/Site.py 2009-01-11 10:49:38.000000000 -0700 +++ b/Mailman/Site.py 2010-01-14 11:59:33.000000000 -0700 @@ -37,7 +37,7 @@ try: omask = os.umask(0) try: - os.makedirs(path, 02775) + os.makedirs(path, 0775) finally: os.umask(omask) except OSError, e: --- a/Makefile.in 2009-01-11 10:49:38.000000000 -0700 +++ b/Makefile.in 2010-01-14 11:59:33.000000000 -0700 @@ -39,7 +39,7 @@ # Customizable but not set by configure OPT= @OPT@ -CFLAGS= @CFLAGS@ $(OPT) $(DEFS) +CFLAGS= @CFLAGS@ $(OPT) $(BI_RC_CFLAGS) $(DEFS) VAR_DIRS= \ logs archives lists locks data spam qfiles \ --- a/bin/check_perms 2009-01-11 10:49:38.000000000 -0700 +++ b/bin/check_perms 2010-01-14 11:59:32.000000000 -0700 @@ -70,8 +70,8 @@ STATE = State() -DIRPERMS = S_ISGID | S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH -QFILEPERMS = S_ISGID | S_IRWXU | S_IRWXG +DIRPERMS = S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH +QFILEPERMS = S_IRWXU | S_IRWXG PYFILEPERMS = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ARTICLEFILEPERMS = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP PRIVATEPERMS = QFILEPERMS @@ -194,7 +194,7 @@ continue if (mode & DIRPERMS) <> DIRPERMS: STATE.ERRORS += 1 - print _('directory must be at least 02775: %(d)s'), + print _('directory must be at least 0775: %(d)s'), if STATE.FIX: print _('(fixing)') os.chmod(d, mode | DIRPERMS) --- a/bin/mailmanctl 2009-01-11 10:49:38.000000000 -0700 +++ b/bin/mailmanctl 2010-01-14 11:59:32.000000000 -0700 @@ -319,6 +319,14 @@ checkprivs = 1 force = 0 quiet = 0 + if not os.path.isdir('/var/run/mailman'): + os.mkdir('/var/run/mailman', 0775) + if os.path.isdir('/var/run/mailman'): + os.chown('/var/run/mailman', 78, 78) + if not os.path.isdir('/var/log/mailman'): + os.mkdir('/var/log/mailman', 0775) + if os.path.isdir('/var/log/mailman'): + os.chown('/var/log/mailman', 78, 78) for opt, arg in opts: if opt in ('-h', '--help'): usage(0) @@ -351,6 +359,185 @@ if not quiet: print _("Shutting down Mailman's master qrunner") kill_watcher(signal.SIGTERM) + elif command == 'startf': + # First, complain loudly if there's no site list. + check_for_site_list() + # Here's the scoop on the processes we're about to create. We'll need + # one for each qrunner, and one for a master child process watcher / + # lock refresher process. + # + # The child watcher process simply waits on the pids of the children + # qrunners. Unless explicitly disabled by a mailmanctl switch (or the + # children are killed with SIGTERM instead of SIGINT), the watcher + # will automatically restart any child process that exits. This + # allows us to be more robust, and also to implement restart by simply + # SIGINT'ing the qrunner children, and letting the watcher restart + # them. + # + # Under normal operation, we have a child per queue. This lets us get + # the most out of the available resources, since a qrunner with no + # files in its queue directory is pretty cheap, but having a separate + # runner process per queue allows for a very responsive system. Some + # people want a more traditional (i.e. MM2.0.x) cron-invoked qrunner. + # No problem, but using mailmanctl isn't the answer. So while + # mailmanctl hard codes some things, others, such as the number of + # qrunners per queue, is configurable in mm_cfg.py. + # + # First, acquire the master mailmanctl lock + lock = acquire_lock(force) + if not lock: + return + # Daemon process startup according to Stevens, Advanced Programming in + # the UNIX Environment, Chapter 13. + #pid = os.fork() + #if pid: + # # parent + # if not quiet: + # print _("Starting Mailman's master qrunner.") + # # Give up the lock "ownership". This just means the foreground + # # process won't close/unlock the lock when it finalizes this lock + # # instance. We'll let the mater watcher subproc own the lock. + # lock._transfer_to(pid) + # return + # child + #lock._take_possession() + # First, save our pid in a file for "mailmanctl stop" rendezvous. We + # want the perms on the .pid file to be rw-rw---- + omask = os.umask(6) + try: + fp = open(mm_cfg.PIDFILE, 'w') + print >> fp, os.getpid() + fp.close() + finally: + os.umask(omask) + # Create a new session and become the session leader, but since we + # won't be opening any terminal devices, don't do the ultra-paranoid + # suggestion of doing a second fork after the setsid() call. + #os.setsid() + # Instead of cd'ing to root, cd to the Mailman installation home + #os.chdir(mm_cfg.PREFIX) + # Set our file mode creation umask + #os.umask(007) + # I don't think we have any unneeded file descriptors. + # + # Now start all the qrunners. This returns a dictionary where the + # keys are qrunner pids and the values are tuples of the following + # form: (qrname, slice, count). This does its own fork and exec, and + # sets up its own signal handlers. + kids = start_all_runners() + # Set up a SIGALRM handler to refresh the lock once per day. The lock + # lifetime is 1day+6hours so this should be plenty. + def sigalrm_handler(signum, frame, lock=lock): + lock.refresh() + signal.alarm(mm_cfg.days(1)) + signal.signal(signal.SIGALRM, sigalrm_handler) + signal.alarm(mm_cfg.days(1)) + # Set up a SIGHUP handler so that if we get one, we'll pass it along + # to all the qrunner children. This will tell them to close and + # reopen their log files + def sighup_handler(signum, frame, kids=kids): + # Closing our syslog will cause it to be re-opened at the next log + # print output. + syslog.close() + for pid in kids.keys(): + os.kill(pid, signal.SIGHUP) + # And just to tweak things... + syslog('qrunner', + 'Master watcher caught SIGHUP. Re-opening log files.') + signal.signal(signal.SIGHUP, sighup_handler) + # We also need to install a SIGTERM handler because that's what init + # will kill this process with when changing run levels. + def sigterm_handler(signum, frame, kids=kids): + for pid in kids.keys(): + try: + os.kill(pid, signal.SIGTERM) + except OSError, e: + if e.errno <> errno.ESRCH: raise + syslog('qrunner', 'Master watcher caught SIGTERM. Exiting.') + signal.signal(signal.SIGTERM, sigterm_handler) + # Finally, we need a SIGINT handler which will cause the sub-qrunners + # to exit, but the master will restart SIGINT'd sub-processes unless + # the -n flag was given. + def sigint_handler(signum, frame, kids=kids): + for pid in kids.keys(): + os.kill(pid, signal.SIGINT) + syslog('qrunner', 'Master watcher caught SIGINT. Restarting.') + signal.signal(signal.SIGINT, sigint_handler) + # Now we're ready to simply do our wait/restart loop. This is the + # master qrunner watcher. + try: + while 1: + try: + pid, status = os.wait() + except OSError, e: + # No children? We're done + if e.errno == errno.ECHILD: + break + # If the system call got interrupted, just restart it. + elif e.errno <> errno.EINTR: + raise + continue + killsig = exitstatus = None + if os.WIFSIGNALED(status): + killsig = os.WTERMSIG(status) + if os.WIFEXITED(status): + exitstatus = os.WEXITSTATUS(status) + # We'll restart the process unless we were given the + # "no-restart" switch, or if the process was SIGTERM'd or + # exitted with a SIGTERM exit status. This lets us better + # handle runaway restarts (say, if the subproc had a syntax + # error!) + restarting = '' + if restart: + if (exitstatus == None and killsig <> signal.SIGTERM) or \ + (killsig == None and exitstatus <> signal.SIGTERM): + # Then + restarting = '[restarting]' + qrname, slice, count, restarts = kids[pid] + del kids[pid] + syslog('qrunner', """\ +Master qrunner detected subprocess exit +(pid: %d, sig: %s, sts: %s, class: %s, slice: %d/%d) %s""", + pid, killsig, exitstatus, qrname, + slice+1, count, restarting) + # See if we've reached the maximum number of allowable restarts + if exitstatus <> signal.SIGINT: + restarts += 1 + if restarts > MAX_RESTARTS: + syslog('qrunner', """\ +Qrunner %s reached maximum restart limit of %d, not restarting.""", + qrname, MAX_RESTARTS) + restarting = '' + # Now perhaps restart the process unless it exited with a + # SIGTERM or we aren't restarting. + if restarting: + newpid = start_runner(qrname, slice, count) + kids[newpid] = (qrname, slice, count, restarts) + finally: + # Should we leave the main loop for any reason, we want to be sure + # all of our children are exited cleanly. Send SIGTERMs to all + # the child processes and wait for them all to exit. + for pid in kids.keys(): + try: + os.kill(pid, signal.SIGTERM) + except OSError, e: + if e.errno == errno.ESRCH: + # The child has already exited + syslog('qrunner', 'ESRCH on pid: %d', pid) + del kids[pid] + # Wait for all the children to go away + while 1: + try: + pid, status = os.wait() + except OSError, e: + if e.errno == errno.ECHILD: + break + elif e.errno <> errno.EINTR: + raise + continue + # Finally, give up the lock + lock.unlock(unconditionally=1) + os._exit(0) elif command == 'restart': # Sent the master qrunner process a SIGHUP. This will cause the # master qrunner to kill and restart all the worker qrunners, and to --- a/bin/update 2009-01-11 10:49:38.000000000 -0700 +++ b/bin/update 2010-01-14 11:59:32.000000000 -0700 @@ -234,7 +234,7 @@ # if not os.path.exists(mbox_dir): ou = os.umask(0) - os.mkdir(mbox_dir, 02775) + os.mkdir(mbox_dir, 0775) os.umask(ou) else: # this shouldn't happen, but hey, just in case @@ -244,7 +244,7 @@ b6, so I'm renaming it to %(mbox_dir)s.tmp and proceeding.""") os.rename(mbox_dir, "%s.tmp" % (mbox_dir)) ou = os.umask(0) - os.mkdir(mbox_dir, 02775) + os.mkdir(mbox_dir, 0775) os.umask(ou) # Move any existing mboxes around, but watch out for both a public and a @@ -333,7 +333,7 @@ # # chmod the html archives # - os.chmod(html_dir, 02775) + os.chmod(html_dir, 0775) # BAW: Is this still necessary?! mlist.Save() # @@ -385,9 +385,9 @@ abs = os.path.join(dir, f) if os.path.isdir(abs): if f == "database": - os.chmod(abs, 02770) + os.chmod(abs, 0770) else: - os.chmod(abs, 02775) + os.chmod(abs, 0775) elif os.path.isfile(abs): os.chmod(abs, 0664) --- a/configure 2009-01-11 10:49:38.000000000 -0700 +++ b/configure 2010-01-14 11:59:33.000000000 -0700 @@ -3329,11 +3329,9 @@ if mailmangid <> gid: problems.append("Directory must be owned by group " + groupname + ": " + prefix) - if (mode & S_ISGID) <> S_ISGID: - problems.append("Set-gid bit must be set for directory: " + prefix) perms = S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH if (mode & perms) <> perms: - problems.append("Permissions should be at least 02775: " + prefix) + problems.append("Permissions should be at least 0775: " + prefix) if not problems: msg = "okay\n" else: --- a/configure.in 2009-01-11 10:49:38.000000000 -0700 +++ b/configure.in 2010-01-14 11:59:33.000000000 -0700 @@ -394,11 +394,9 @@ if mailmangid <> gid: problems.append("Directory must be owned by group " + groupname + ": " + prefix) - if (mode & S_ISGID) <> S_ISGID: - problems.append("Set-gid bit must be set for directory: " + prefix) perms = S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH if (mode & perms) <> perms: - problems.append("Permissions should be at least 02775: " + prefix) + problems.append("Permissions should be at least 0775: " + prefix) if not problems: msg = "okay\n" else: --- a/contrib/check_perms_grsecurity.py 2009-01-11 10:49:38.000000000 -0700 +++ b/contrib/check_perms_grsecurity.py 2010-01-14 11:59:27.000000000 -0700 @@ -82,7 +82,7 @@ for dir in dirstochownroot: dirpath = paths.prefix + '/' + dir os.chown(dirpath, 0, gid) - os.chmod(dirpath, 02755) + os.chmod(dirpath, 0755) print dirpath print --- a/misc/Makefile.in 2009-01-11 10:49:38.000000000 -0700 +++ b/misc/Makefile.in 2010-01-14 11:59:25.000000000 -0700 @@ -98,7 +98,7 @@ for p in $(PACKAGES); \ do \ gunzip -c $(srcdir)/$$p.tar.gz | (cd $(PKGDIR) ; tar xf -); \ - (cd $(PKGDIR)/$$p ; umask 02 ; PYTHONPATH=$(PYTHONLIBDIR) $(PYTHON) $(SETUPCMD)); \ + (cd $(PKGDIR)/$$p ; umask 02 ; PYTHONPATH=$(PYTHONLIBDIR) CFLAGS="$(BI_RC_CFLAGS)" $(PYTHON) $(SETUPCMD)); \ done finish: --- a/src/Makefile.in 2009-01-11 10:49:38.000000000 -0700 +++ b/src/Makefile.in 2010-01-14 11:59:24.000000000 -0700 @@ -42,7 +42,7 @@ # Customizable but not set by configure OPT= @OPT@ -CFLAGS= @CFLAGS@ $(OPT) $(DEFS) $(LIBS) +CFLAGS= @CFLAGS@ $(BI_RC_CFLAGS) $(OPT) $(DEFS) $(LIBS) CGIDIR= $(exec_prefix)/cgi-bin CGIEXT= @CGIEXT@ MAILDIR= $(exec_prefix)/mail