📄 qmail-queue.c
字号:
//程序主要完成的功能是:
//1.生成自己的邮件首部,也就是你在邮件头中见到的类似下面的东西
//Recevied (qmail 855 invoked by uid 0); 2 May 2003 12:18:09 -0000
//2.建立3个文件
// queue/mess 邮件正文
// queue/intd 用户id,进程id,mailform,rcptto
// queue/todo 是intd目录下文件的复本
//3.写命名管道lock/trigger通知新邮件
#include <sys/types.h>#include <sys/stat.h>#include "readwrite.h"#include "sig.h"#include "exit.h"#include "open.h"#include "seek.h"#include "fmt.h"#include "alloc.h"#include "substdio.h"#include "datetime.h"#include "now.h"#include "triggerpull.h"#include "extra.h"#include "auto_qmail.h"#include "auto_uids.h"#include "date822fmt.h"#include "fmtqfn.h"#define DEATH 86400 /* 24 hours; _must_ be below q-s's OSSIFIED (36 hours) */#define ADDR 1003char inbuf[2048];struct substdio ssin;char outbuf[256];struct substdio ssout;datetime_sec starttime;struct datetime dt;unsigned long mypid;unsigned long uid;char *pidfn;struct stat pidst;unsigned long messnum;char *messfn;char *todofn;char *intdfn;int messfd;int intdfd;int flagmademess = 0;int flagmadeintd = 0;
// 错误清理void cleanup(){ if (flagmadeintd) { seek_trunc(intdfd,0); if (unlink(intdfn) == -1) return; } if (flagmademess) { seek_trunc(messfd,0); if (unlink(messfn) == -1) return; }}void die(e) int e; { _exit(e); }void die_write() { cleanup(); die(53); }void die_read() { cleanup(); die(54); }void sigalrm() { /* thou shalt not clean up here */ die(52); }void sigbug() { die(81); }unsigned int receivedlen;char *received;/* "Received: (qmail-queue invoked by alias); 26 Sep 1995 04:46:54 -0000\n" */static unsigned int receivedfmt(s)char *s;{ unsigned int i; unsigned int len; len = 0;
//生成 26 Sep 1995 04:46:54 -0000 的形式 i = fmt_str(s,"Received: (qmail "); len += i; if (s) s += i; i = fmt_ulong(s,mypid); len += i; if (s) s += i; i = fmt_str(s," invoked "); len += i; if (s) s += i; if (uid == auto_uida) { i = fmt_str(s,"by alias"); len += i; if (s) s += i; } else if (uid == auto_uidd) { i = fmt_str(s,"from network"); len += i; if (s) s += i; } else if (uid == auto_uids) { i = fmt_str(s,"for bounce"); len += i; if (s) s += i; } else { i = fmt_str(s,"by uid "); len += i; if (s) s += i; i = fmt_ulong(s,uid); len += i; if (s) s += i; } i = fmt_str(s,"); "); len += i; if (s) s += i; i = date822fmt(s,&dt); len += i; if (s) s += i; return len;}void received_setup(){ receivedlen = receivedfmt((char *) 0); received = alloc(receivedlen + 1); if (!received) die(51); receivedfmt(received);}unsigned int pidfmt(s,seq)char *s;unsigned long seq;{ unsigned int i; unsigned int len;
//生成类型pid/3434.34242424.1的字符串到s中
//这个字符串实际上就是/var/qmail/queue/pid目录下一个文件名,指示当前进程的pid len = 0; i = fmt_str(s,"pid/"); len += i; if (s) s += i; i = fmt_ulong(s,mypid); len += i; if (s) s += i; i = fmt_str(s,"."); len += i; if (s) s += i; i = fmt_ulong(s,starttime); len += i; if (s) s += i; i = fmt_str(s,"."); len += i; if (s) s += i; i = fmt_ulong(s,seq); len += i; if (s) s += i; ++len; if (s) *s++ = 0; return len;}char *fnnum(dirslash,flagsplit)char *dirslash;int flagsplit;{ char *s; s = alloc(fmtqfn((char *) 0,dirslash,messnum,flagsplit)); if (!s) die(51); fmtqfn(s,dirslash,messnum,flagsplit); return s;}
//建立类似/var/run/inet.pid之类的进程id文件void pidopen(){ unsigned int len; unsigned long seq; seq = 1; len = pidfmt((char *) 0,seq); pidfn = alloc(len); if (!pidfn) die(51); for (seq = 1;seq < 10;++seq) { if (pidfmt((char *) 0,seq) > len) die(81); /* paranoia */ pidfmt(pidfn,seq); messfd = open_excl(pidfn); if (messfd != -1) return; } die(63);}char tmp[FMT_ULONG];void main(){ unsigned int len; char ch; sig_blocknone(); umask(033); if (chdir(auto_qmail) == -1) die(61); if (chdir("queue") == -1) die(62); //改变工作目录到/var/qmail/queue mypid = getpid(); uid = getuid(); starttime = now();
datetime_tai(&dt,starttime); //将起始时间转换为可读年月日时分秒的形式
//生成自己的邮件头存入缓存reseived中
//例如:received="Received:(qmail 3434 invoked by 34434); Apr 27 2003 14:55:34" received_setup(); sig_pipeignore(); sig_miscignore(); sig_alarmcatch(sigalrm); //捕捉alarm信号,控制超时 sig_bugcatch(sigbug); alarm(DEATH); //超时秒数,缺省值是86400(24小时)后错误返回52 pidopen(); //建立进程id文件 if (fstat(messfd,&pidst) == -1) die(63); messnum = pidst.st_ino; //进程id文件的inode节点号
/*生成将要建立的文件的文件名
几个文件都是根据刚才建立的pid文件的inode节点号命名的.inode不可能被两个文件同时
占用,这保证了邮件唯一性。其中mess目录下的文件放置有一个%23的问题,
tips: 因为是%23所以该目录名最大的可能只有22,明白queue/mess目录下目录为什么
最大只22了吧 ,比如说inode节点号为3455,那么3455%23=5,
那么将生成/var/qmail/queue/mess/5/3455 这样一个文件来存放邮件。
/var/qmail/queue/todo/3455与/var/qmail/queue/intd/3455是相同的,
都是保存用户id,进程id,mailfrom,rcptto的。
*/ messfn = fnnum("mess/",1); todofn = fnnum("todo/",0); intdfn = fnnum("intd/",0); if (link(pidfn,messfn) == -1) die(64); if (unlink(pidfn) == -1) die(63);
//进程id文件使命很快结束,死掉了
//所以你不应该想在queue/pid目录中找到进程id文件
//另外,qmail-clean也将第七清理queue/pid目录下的pid文件,说定期其实也不是,
//qmail-clean会在每收到30个清理邮件的请求后清理pid目录一次,这在分析qmail-clean
//时我们将会看到 flagmademess = 1;
//fd1关联写mess/下新建的文件,通过管道连接<------qmail-smtp的qqt->fde
//也就是说qmail-smtpd进程写它的qqt-fde,那就相当于写mess/下新建立的邮件
//注意是关联不是正式写 substdio_fdbuf(&ssout,write,messfd,outbuf,sizeof(outbuf));
//fd0关联到读标准输入到缓存区inbuf通过管道连接<-----qmail-smtp的qqt->fdm
//也就是说读ssin将从qmail-smtpd的qqt->fdm端读 substdio_fdbuf(&ssin,read,0,inbuf,sizeof(inbuf));
//向mess/下的邮件文件写qmail-queue的头部信息 if (substdio_bput(&ssout,received,receivedlen) == -1) die_write();
//从fd1读smtpd设置的邮件首部 switch(substdio_copy(&ssout,&ssin)) { case -2: die_read(); case -3: die_write(); } if (substdio_flush(&ssout) == -1) die_write(); if (fsync(messfd) == -1) die_write(); intdfd = open_excl(intdfn); if (intdfd == -1) die(65); flagmadeintd = 1;
//fd1关联到写intd/下新建立的文件 fd0关联到读inbuff缓冲区 substdio_fdbuf(&ssout,write,intdfd,outbuf,sizeof(outbuf)); substdio_fdbuf(&ssin,read,1,inbuf,sizeof(inbuf));
/*
向intd下新建立的文件写如下格式内容
这些内容来自于qmail-smtpd.c中的data命令的解释函数。
u[uid]p[pid]F[mailfrom]T[rcptto1][rcptto2][rcptton]
例如:lyx@hg.org向hong@hg.org和beggar@hg.org发邮件可能会有如下内容
u6027p34234Flyx@hg.orgThong@hg.orgTbeggar@hg.org
*/ if (substdio_bput(&ssout,"u",1) == -1) die_write(); if (substdio_bput(&ssout,tmp,fmt_ulong(tmp,uid)) == -1) die_write(); if (substdio_bput(&ssout,"",1) == -1) die_write(); if (substdio_bput(&ssout,"p",1) == -1) die_write(); if (substdio_bput(&ssout,tmp,fmt_ulong(tmp,mypid)) == -1) die_write(); if (substdio_bput(&ssout,"",1) == -1) die_write(); if (substdio_get(&ssin,&ch,1) < 1) die_read(); if (ch != 'F') die(91); if (substdio_bput(&ssout,&ch,1) == -1) die_write(); for (len = 0;len < ADDR;++len) { if (substdio_get(&ssin,&ch,1) < 1) die_read(); if (substdio_put(&ssout,&ch,1) == -1) die_write(); if (!ch) break; }
//如有多个邮件接收人时,这些接收人的地址总长度不能超过1023字节,如果每个
//邮件地址约为15个字节的话,大约可能指定65个 if (len >= ADDR) die(11); if (substdio_bput(&ssout,QUEUE_EXTRA,QUEUE_EXTRALEN) == -1) die_write(); for (;;) { if (substdio_get(&ssin,&ch,1) < 1) die_read(); if (!ch) break; if (ch != 'T') die(91); if (substdio_bput(&ssout,&ch,1) == -1) die_write(); for (len = 0;len < ADDR;++len) { if (substdio_get(&ssin,&ch,1) < 1) die_read(); if (substdio_bput(&ssout,&ch,1) == -1) die_write(); if (!ch) break; } if (len >= ADDR) die(11); } if (substdio_flush(&ssout) == -1) die_write(); if (fsync(intdfd) == -1) die_write();
//复制intdfn到todofn 由此可见这两个是相同的文件 if (link(intdfn,todofn) == -1) die(66);
//向命名管道/var/qmail/queue/lock/trigger写一个字节(写的是0),通知有新的邮件 triggerpull(); die(0); //退出}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -