root/trunk/kbs_bbs/doc/kbsIntro.txt

Revision 10425, 29.2 KB (checked in by fancy, 23 months ago)

doc update

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1$Id$
2
3发信人: atppp
4标  题: [kbsIntro] kbs 系统入门
5发信站: http://dev.kcn.cn/
6
70. 序
8
90.0 声明
10
11尽管 kbs 系统基础数据结构一般很少变化,但是本文档只针对 kbs SVN 主分支目前的代码,不保证对其它时间或其它分支代码的正确性,也不对可能造成的任何后果负责。本文可以在网络上自由转载,但请保留全文的完整性。
12
130.1 本文说明
14
15文中有一些对数据结构的说明。大多数结构定义在 src/struct.h 中,char[] 的字段若非特别说明都是以 '\0' 结束的字符串。对本文或相关问题的疑问,欢迎到水木社区 (newsmth.net) BBSMan_Dev 版讨论。
16
17
18
191. 版面及文章
20
211.1 文章的存储
22
23每个版面都是一个目录。比如 SYSOP 版的目录是 $BBSHOME/boards/SYSOP/。在这个目录下面有这个版面的文章及索引,每一篇文章都是一个文件,文件名大致是这样子:
24
25M.1085385291.w0
26             ^^ 用于区分同一个时间点的多个帖子
27  ^^^^^^^^^^ 10 位数的 timestamp,帖子发表时间
28^ M 是文件名前缀,不同性质的文件,前缀就不一样
29
30下面是各版面索引文件和相应文件名的前缀:(通常情况下)
31版面文章   .DIR        M
32置顶文章   .DINGDIR    Z (硬连接到相应 M 前缀文件)
33文摘区     .DIGEST     G (硬连接到相应 M 前缀文件)
34回收站     .DELETED    D
35废纸篓     .JUNK       J
36
37同主题模式依赖于版面目录下面的一个 .ORIGIN 文件。这个文件就是 .DIR 文件里面所有原作(id==groupid,参考 1.3 节)的 fileheaders。
38
39另外,在 $BBSHOME/vote/ 下面每个版面也会有一个目录,主要用于储存投票等数据,按照 KCN 的精神,今后最好不要再在 vote 目录下储存版面信息。
40
41
421.2 版面文章索引,fileheader 结构
43
44每个索引文件就是多个 fileheader 结构。这个 fileheader 定义在 src/default.h 或者 src/site.h 里面,具体解释如下:
45
46typedef struct fileheader {
47    char filename[FILENAME_LEN];
48        帖子的文件名,比方 1.1 节那个例子,这个字段就是 M.1085385291.w0。
49        kbs 也可以采用版面目录下 52 个子目录分散储存帖子的方法(参考
50        feeling.h 内 GET_POSTFILENAME 宏定义),这种情况下,本字段的形式
51        就是 A/M.1085385291.w0。
52    unsigned int id, groupid, reid;
53        这三个 id 字段非常重要,下面一节具体说明。
54    int o_bid;
55    unsigned int o_id;
56    unsigned int o_groupid;
57    unsigned int o_reid;
58        以上四个字段用于需审核的文章和推荐文章,o_bid 是原文的版面 bid。
59        后面三个是原文在原版面 .DIR 中相应的三个 id 字段值。
60    char innflag[2];
61        帖子是否转信。"LL" 表示本地发表,"SS" 表示转信,"\0M" 表示外面
62        转进来的文章。
63    char owner[OWNER_LEN];
64        发文作者。
65    unsigned int eff_size;
66        帖子的有效字节数。
67    time_t posttime;
68        帖子发表时间的 timestamp。在使用 52 个子目录分散储存帖子的情况下该
69        字段尤其重要。
70    long attachment;
71        附件位置偏移量,参考下面的附件格式。
72    char title[ARTICLE_TITLE_LEN];
73        帖子的标题。注意超长标题需要截短。
74    unsigned short replycount;
75        主题帖的回复数,在 .DIR 和 .ORIGIN 中同步更新,对于其他模式和非主题
76        贴,该字段无用。
77    char unused[114];
78        没有用到,为了将来需要升级该结构体时省却转换索引的麻烦,所以留点地方。
79    unsigned char accessed[4];
80        一些帖子的属性,前两个元素是 bitwise-OR 的属性,包括:
81        accessed[0]: FILE_SIGN     (0x01) 版主设置的 # 标记
82                     FILE_TOTAL    (0x02) 已被制作合集
83                     FILE_PERCENT  (0x04) 版主设置的 % 标记
84                     FILE_MARKED   (0x08) 被 m 的帖子
85                     FILE_DIGEST   (0x10) 文摘区贴子(被 g 的帖子)
86                     FILE_IMPORTED (0x80) 已被收录精华
87        accessed[1]: FILE_READ     (0x01) 不可 re
88                     FILE_DEL      (0x02) 版主标记删除 X
89                     FILE_MAILBACK (0x04) 回帖抄送信箱
90                     FILE_COMMEND  (0x08) 已被推荐文章
91                     FILE_CENSOR   (0x20) FILTER 版面需审核文章
92                     FILE_TEX      (0x80) tex 方式发表帖子
93        accessed[3]: 回收站的 .DIR 中这个字段为:
94                     删除时间 timestamp / (3600 * 24) % 100
95                     主要用于定时删除老的删帖。
96} fileheader;
97
98
991.3 id, groupid, reid 三个字段
100
101这三个字段是帖子的索引 ID 和同主题信息。.DIR 里面 fileheader 结构的 id 字段依次递增。注意一定是递增,否则 web 下浏览会不正常!另外两个字段的作用举例如下:
102
103有人新发表了帖子 A,这个帖子系统自动给了 id = 10
104然后有人回复帖子 A,我们叫它帖子 B;
105再有人回复了帖子 B,我们叫它帖子 C;
106最后有人回复帖子 A,称为帖子 D。这四个帖子的三个字段会是这样:
107
108帖子    id      groupid     reid
109================================
110 A      10      10          10
111 B      11      10          10
112 C      12      10          11
113 D      13      10          10
114
115其中,groupid 就是用来判断帖子同主题的,注意,帖子同主题和帖子标题无关。reid 用来产生回复树结构。
116
117
1181.4 附件结构
119
120附件的内容就添加在它所属帖子的那个文件的末尾。有附件的帖子文件其组成是:帖子正文(最后是一个回车符),附件一,附件二,...其中每个附件段是由四个部分组成的:
121*) 第一部分:八个 '\0' 字节。
122*) 第二部分:附件的原始文件名字符串,可以含有中文字符,长度不应该超过 40 个字
123   符。文件名的后缀将决定附件类型。本部分长度不定,所以千万不要忘记最后的 '\0'
124   字符串结束符。
125*) 第三部分:四个字节,unsigned int 二进制格式整数,表示本附件的长度;
126   注意这个整数是网络字节序(big-endian)存储,也即 MSB 在先。
127*) 第四部分:二进制格式储存的这个附件,本部分长度由第三部分决定。
128另外,这个帖子在 .DIR 里面相应的那个 fileheader 结构的 attachment 字段应该设置为第一个附件段的起始偏移量(ftell)。参考:
129libBBS/article.c upload_post_append() 和 get_mimetype() 函数
130
131
1321.5 .BOARDS 文件,boardheader 结构
133
134$BBSHOME/.BOARDS 文件是所有版面的信息,实际上是 MAXBOARD 个 boardheader 结构。在 kbs 系统内部每个版面都有一个版面号 bid,这个 bid 就是该版面在 .BOARDS 里面的位置,注意版面号是 1-based 的。
135
136struct boardheader {
137    char filename[STRLEN];
138            版面的英文名称,STRLEN 是 80
139    char BM[BM_LEN];
140            版主列表,BM_LEN 是 60。多个版主用空格隔开
141    char title[STRLEN];
142            版面的说明,格式是 a[bbbb]ccccccdddd...
143                a: 讨论区分区
144                bbbb: 讨论区分类
145                cccccc: 转信标签,一般设置为六个空格
146                dddd....: 讨论区说明,也即通常所说的版面中文名称
147            比方:
148                0[站务]      测试用版
149    unsigned level;
150            版面存取权限。
151    unsigned int idseq;
152            当前已经使用到的 id 值,参考 boardstatus 结构 nowid 字段。
153    unsigned int clubnum;
154            俱乐部序号。0 表示这个版面不是俱乐部。
155    unsigned int flag;
156            版面的一些属性比方是否参与转信。
157    union {
158    unsigned int adv_club;
159    unsigned int group_total;
160    } board_data;
161    time_t createtime;
162            版面创建时间,“新开启的讨论区”会用到。
163    int unused;
164    char ann_path[128];
165            精华区路径。实际精华区绝对路径是:
166            $BBSHOME/0Announce/groups/<ann_path>
167    int group;
168            所属目录。
169    char title_level;
170            设定用户需要什么身份可见这个版面。0 表示没有限制。
171    char des[195];
172            版面描述,用于 www 的版面说明和版面超级搜索。
173#ifdef FLOWBANNER
174    int bannercount;
175    char banners[MAXBANNER][BANNERSIZE];
176#endif
177};
178
179
180
1812. 用户
182
1832.1 什么样的 ID 在 kbs 系统中是合法的
184
185kbs 系统合法 ID 的规则是:至少 2 个字符,至多 12 个字符。第一个字符必须是字母,后面的字符必须是字母或者数字。
186
187
1882.2 用户相关的文件系统结构
189
190$BBSHOME/.PASSWDS 是用户帐号基本信息,实质上是 MAXUSERS 个 userec 结构,结构说明后面详细写。另外,每个用户还有一个 home 目录和一个信件目录,比方 atppp 用户这两个目录分别是 $BBSHOME/home/A/atppp/ 和 $BBSHOME/mail/A/atppp/。用户 home 目录下有一些杂七杂八的文件,其中有一个文件叫做 .userdata,这个是用户帐号的补充信息,实际上就是一个 userdata 结构,结构说明后面详细写。信件目录到后面一大章节再说。
191
192
1932.3 userec 结构
194
195struct userec {
196    char userid[IDLEN + 2];
197        用户名。IDLEN 是 12,不要轻易修改。
198    char flags;
199        一些标志,戒网,版面排序之类的。转换用户建议设置成 0x81,也就是
200            PAGER_FLAG | CURSOR_FLAG
201        参考源代码 contrib/fb2k2smth/README 相关说明。
202            #define PAGER_FLAG   0x1        /* true if pager was OFF last session */
203            #define CLOAK_FLAG   0x2        /* true if cloak was ON last session */
204            #define BRDSORT_FLAG 0x20       /* true if the boards sorted alphabetical */
205            #define CURSOR_FLAG  0x80       /* true if the cursor mode open */
206            #define GIVEUP_FLAG  0x4        /* true if the user is giving up  by bad 2002.7.6 */
207            #define PCORP_FLAG   0x40       /* true if have personalcorp */
208    unsigned char title;
209        用户身份
210    time_t firstlogin;
211        注册时间或者第一次登录的时间戳。
212    char lasthost[16];
213        最后登录的 IP。
214    unsigned int numlogins;
215        登录次数。
216    unsigned int numposts;
217        发帖数。
218#ifdef CONV_PASS
219    char passwd[OLDPASSLEN];
220    char unused_padding[2];
221#endif
222    char username[NAMELEN];
223        用户昵称。
224    unsigned int club_read_rights[MAXCLUB>>5];
225    unsigned int club_write_rights[MAXCLUB>>5];
226        这两个是俱乐部读写权限。如果该用户的
227            club_read_rights[(clubnum-1)>>5]&(1<<((clubnum-1)&0x1f))
228        为真,则这个用户可以读取俱乐部号为 clubnum 的版面。参考函数
229            check_read_perm() haspostperm()
230        注:这里的数据结构决定了 MAXCLUB 必须定义为 32 的倍数。
231    unsigned char md5passwd[MD5PASSLEN];
232        md5 消化过的密码。密码问题后面会一并详细说明。
233        此字符串定长 16,最后不以 '\0' 结束!
234    unsigned userlevel;
235        用户权限。
236    time_t lastlogin;
237        上次登录的时间戳。
238    time_t stay;
239        总上线时间。单位是秒。
240    int signature;
241        当前使用的签名档号码。
242    unsigned int userdefine[2];
243        用户自定义参数。新注册用户是 0xffffffff,但是 wForum 的标准应该是默认
244        关闭公布详细信息,userdefine[0] 的实际初始值是
245            0xFFFFFFFF & (~DEF_SHOWREALUSERDATA)
246        也就是 0xBFFFFFFF。
247    time_t notedate;
248    int noteline;
249        上面两个都和查看留言板相关。
250    int unused_atppp;
251    time_t exittime;
252        上次退出登录的时间戳。
253    unsigned int usedspace;
254        用户信件使用的磁盘空间。
255    int unused[7];
256};
257
2582.4 userdata 结构
259
260userdata 是用户 home 目录下 .userdata 文件的结构。另外用户 home 目录下还有一个文件是 usermemo(用来 mmap 的),这个文件的内容要和 .userdata 严格一致,如果 usermemo 文件不存在系统会自动从 .userdata 建立,所以如果 usermemo 存在且和 .userdata 不一致的话系统就会出错。
261
262userdata 结构:
263
264struct userdata
265{
266    char userid[IDLEN + 2];
267        用户名。
268    char __reserved[2];
269    char realemail[STRLEN - 16];
270        真实 email。
271    char realname[NAMELEN];
272        真实姓名。
273    char address[STRLEN];
274        通讯地址。
275    char email[STRLEN];
276        email。
277#ifdef HAVE_BIRTHDAY
278    char            gender;
279        性别,写 'M' 或者 'F'。如果不是这两个字符可能出错。
280    unsigned char   birthyear;
281        出生年的后两位。
282    unsigned char   birthmonth;
283        出生月。
284    unsigned char   birthday;
285        出生日。上面三个字段注意类型是 unsigned char。
286#endif
287    char reg_email[STRLEN];
288        注册使用的 email。
289/*#ifdef SMS_SUPPORT*/
290    bool mobileregistered;
291    char mobilenumber[MOBILE_NUMBER_LEN];
292/*#endif*/
293/* add by roy 2003.07.23 for wForum */
294    char OICQ[STRLEN];
295    char ICQ[STRLEN];
296    char MSN[STRLEN];
297    char homepage[STRLEN];
298    int userface_img;
299        设置成 0。如果有自定义头像,设置成 -1。
300    char userface_url[STRLEN];
301        这个是自定义头像的完整 URL 地址。
302    unsigned char userface_width;
303    unsigned char userface_height;
304        上面两个字段是自定义头像的长和宽。必须是 0~120 之间的整数。
305    unsigned int group;
306    char country[STRLEN];
307    char province[STRLEN];
308    char city[STRLEN];
309    unsigned char shengxiao;
310    unsigned char bloodtype;
311    unsigned char religion;
312    unsigned char profession;
313    unsigned char married;
314    unsigned char education;
315    char graduateschool[STRLEN];
316    unsigned char character;
317    char photo_url[STRLEN];
318        个人相片的完整 URL 地址。
319    char telephone[STRLEN];
320    char smsprefix[41];
321    char smsend[41];
322    unsigned int smsdef;
323        上面这堆 "add by roy" 的东西目前只在 wForum 里面用到。
324    int signum;
325        签名档个数。
326    int this_field_is_reserved_by_atppp;
327    time_t lastinvite;
328};
329
3302.5 密码
331
332kbs 的用户密码使用 md5 加密储存于 userec 结构的 md5passwd 字段内,但是 kbs 系统并不是对用户密码直接 md5 加密处理,md5passwd 字段是下面这四个字符串顺序连接起来的字符串的 md5:
333    passmagic 密码 passmagic 用户名
334其中 passmagic 是(不包括前后两个引号):
335    "wwj&kcn4SMTHBBS MD5 p9w2d gen2rat8, //grin~~, 2001/5/7"
336相关代码请看 libBBS/pass.c igenpass() 函数。注意 md5passwd 字段的类型是 unsigned char md5passwd[16],也就是所谓的 raw-binary format,而不是有些 md5 程序返回的 32 个字符的字符串。注:由于这个 md5passwd 消化了用户名,所以用户名更改大小写之后必须重新给该用户设置密码。
337
338
3392.6 用户 home 目录下面其它一些文件的说明
340
3412.6.1 .boardrc.gz 已读记录
342
343用户 home 目录下面的 .boardrc.gz 存储用户的已读记录,它是一个使用 gzip 压缩的文件,解压后的长度是
344    BRC_FILESIZE = MAXBOARD * BRC_MAXNUM * sizeof(unsigned int)。
345BRC_MAXNUM 默认是 50,这个文件分为 MAXBOARD 段,第 i 段就是 bid = i 的那个版面的已读记录;每个版面的已读记录就是 BRC_MAXNUM 个非负整数:
346    n1 n2 n3 ... np 0 ... 0
347其中 n1 > n2 > n3 > ... > np > 0。这组已读记录的意义是,该版面 id > n1 的文章都是未读的,id < np 的文章都是已读的;而 np <= id <= n1 的文章中,只有
348    id = n1,n2,n3,...,np
349的文章才是已读的,其余全部未读。已读记录用这个方法来存储是有利有弊的,最大的好处就是比较有效的记录了用户最需要的那部分已读记录,
350
3512.6.2 favboard 收藏版面
352
353用户自定义了收藏版面之后会在用户 home 目录下创建文件 favboard。该文件的格式可以参考 libBBS/boards.c load_myboard1() save_favboard1() 函数。favboard 文件有多种允许的格式,而且 kbs 支持复杂的多目录层次收藏夹结构。下面只说明其中一种格式。favboard 文件可以是这样一个数据结构:
354
355struct {
356    int magic_version_number;
357        写 0x8081
358    int favbrd_list_t;
359        收藏目录个数,写 1
360    struct favbrd_struct fav_boards;
361        具体的收藏版面
362};
363
364favbrd_struct 具体的数据结构如下:
365
366struct favbrd_struct {
367    int bnum;
368        本目录中收藏版面的个数,决定下一个数组字段中多少个元素是有效的
369    int bid[MAXBOARDPERDIR];
370    /* bid >= 0: 版面
371       bid < 0: 目录, 表示子目录是 favbrd_list[-bid]
372       */
373       在不涉及多层目录结构的情况下,bid[i] 表示本目录下第 i 个收藏版面,
374       这里千万注意,bid[i] 是相应版面的 bid - 1,而不是 bid!也就是说,
375       这里 bid[i] 是有可能为 0 的。
376    char title[61];
377    char ename[20];
378    int father;
379        根目录这个字段写 -1。
380    int level;
381};
382
383另外,$BBSHOME/etc/initial_favboard 是新注册用户默认的收藏版面,格式是每行一个版名。如果该文件不存在,默认的收藏版面是 .BOARDS 文件里面的第一个版面。
384
3852.6.3 friends 好友列表
386
387这是 n 个 friends 结构的文件,每个结构都是一个好友:
388
389struct friends {
390    char id[13];
391        好友 id
392    char exp[LEN_FRIEND_EXP];
393        好友说明,可以留空。
394};
395
3962.6.4 plans 个人说明档
397
398这个没什么好说的,就是个人说明档。查询用户的时候会显示出来。
399
4002.6.5 signatures 签名档
401
402这个文件是用户签名档,每六行是一个单位,支持 ansi 控制符,wForum 额外支持少量 ubb。userdata 结构的 signum 字段存储用户签名档个数,如果出现错误可以用recalc_signum 程序来纠正。
403
404
405
4063. 用户站内信件
407
408用户信件,包括信件索引和具体信件内容,全部位于用户信件目录下(参考 2.2 节)。
409
4103.1 信件目录及数据结构
411
412用户信件的总体构架基本类似于讨论区文章。每封信件都是一个文件,文件名的规则和讨论区普通文章的文件名相同。索引文件除了 .DIR 还有两个,如下:
413
414    .DIR        收件箱
415    .SENT       发件箱
416    .DELETED    垃圾箱
417
418这三个索引文件的结构和讨论区文章索引 .DIR 的结构很类似,也是 n 个 fileheader 结构,少数几个字段的意义略有不同,具体解释如下:
419
420typedef struct fileheader {
421    char filename[FILENAME_LEN];
422        帖子的文件名,注意第 3 个字节到第 12 个字节是帖子的发表时间戳。
423    unsigned int id, groupid, reid;
424    int o_bid;
425    unsigned int o_id;
426    unsigned int o_groupid;
427    unsigned int o_reid;
428    char innflag[2];
429        以上八个字段没用。
430    char owner[OWNER_LEN];
431        对方 ID。.DIR 中表示发件人 ID,.SENT 中表示收件人 ID,.DELETED 里面
432        既有可能是发件人也可能是收件人 :(
433    unsigned int eff_size;
434        信件大小。
435    time_t posttime;
436        信件发送时间的 timestamp,好像没用,除非信件也用 52 个子目录存储...
437    long attachment;
438        附件偏移量。
439    char title[ARTICLE_TITLE_LEN];
440        帖子的标题。注意超长标题需要截短。
441    unsigned char accessed[4];
442        一些属性,bitwise-ORs flags:
443        accessed[0]: FILE_READ      (0x01) 已读
444                     FILE_REPLIED   (0x20) 已回复
445                     FILE_MARKED    (0x08) 被 m 的信件
446                     FILE_FORWARDED (0x40) 已转发
447} fileheader;
448
449信件在索引文件中的排序没有特定的规则(并不按照时间排序)。
450
4513.2 自定义信箱
452
453kbs 系统除了上面提到的三个预定义信箱外,还支持用户自定义信箱。载入自定义信箱的代码可以参考 libBBS/record.c load_mail_list() 函数。具体来说,在用户 home 目录下有一个 maildir 文件是自定义信箱的记录,文件结构是:
454struct {
455    int mail_list_t;
456        自定义信箱的个数
457    char mail_list[MAILBOARDNUM][40];
458        每个自定义信箱的具体配置,每个配置是一个 40 个字节的字符串。
459        0~29 字节是信箱名称。30~39 字节是该信箱索引文件的后半段名称。
460        比如,这个字符串前半段是“KCN 情书”,30~39 字节是“MAILBOX1”,
461        那么这个自定义信箱的显示名称就是“KCN 情书”,而索引文件的名称
462        就是 .MAILBOX1,注意文件名第一个字符是附加上去的点。
463};
464
4653.3 信箱属性
466
467在用户 home 目录下有一个文件 .mailbox.prop 是用户信箱的选项配置。该文件就是一个 int 变量,bitwise-ORs 以下属性:
468
469#define MBP_SAVESENTMAIL      0x00000001 //发信时保存信件到发件箱
470#define MBP_FORCEDELETEMAIL   0x00000002 //删除信件时不保存到垃圾箱
471#define MBP_MAILBOXSHORTCUT   0x00000004
472        //版面按 'v' 时进入: 收件箱(OFF) / 信箱主界面(ON)
473
474如果用户 home 目录下 .mailbox.prop 文件不存在,系统自动使用 MBP_DEFAULT 作为信箱选项配置。
475
476
477
478
4794. 共享内存结构
480
481共享内存在 kbs 系统中主要用于进程间通信。比方,在 web 注册了之后,马上就能在 telnet 下登录了,这是因为 web 注册的那个代码修改了相应的共享内存数据,当在 telnet 试图登录的时候,代码就能在共享内存中找到这个信息。当然,这些工作完全可以用文件系统来做,但是用共享内存来做进程间通信效率就会高很多。BBS 的很多重要数据都在共享内存里面,比如两个重要的系统文件:
482
483$BBSHOME/.PASSWDS 这个是用户的帐号信息,包括密码。
484$BBSHOME/.BOARDS  所有版面的信息。
485
486当系统正常启动之后,这两个文件的信息在共享内存里面,系统会定时写磁盘同步数据。当系统正常运行的时候,直接打开这两个文件修改是不对的!BBS 程序如有基础结构变动的更新,一般必须要停掉 BBS 服务,清除掉共享内存数据。kbs 系统使用的主要共享内存列举如下,其中标识是可以在 sysconf.ini 定义的共享内存 key,如无定义则使用默认。
487
488程序变量          说明         要处理代码文件   标识及默认
489======================================================================
490bcache        .BOARDS 的 mmap     bcache.c      只是一个 mmap
491brdshm        版面状态            bcache.c      BCACHE_SHMKEY   0xe6d
492uidshm        用户信息            ucache.c      UCACHE_SHMKEY   0xe70
493utmpshm       登录用户状态信息    utmp.c        UTMP_SHMKEY     0xe73
494utmphead      登录表              utmp.c        UTMPHEAD_SHMKEY 0xe72
495wwwguest_shm  wwwguest 登录信息   bbslib.c      WWWGUEST_SHMKEY 0x1194
496publicshm     全局信息            stuff.c       0xe74 (*)
497
498(*) 援引 bbs.h 注释:
499#define PUBLIC_SHMKEY   3700
500/*这个是唯一一个定义死的SHMKEY,因为sysconf_eval需要
501public shm,而attach shm又需要sysconf_eval,ft*/
502
503
5044.1 publicshm 共享内存结构 public_data
505
506struct public_data {
507    time_t nowtime;
508        当前时间。BBS 系统有大量取当前时间的调用,全部从这里取可以提高效率。
509        这个时间由 miscd 中的 timed 进程负责和系统时钟同步。从这里可以知道
510        如果 timed 进程不正常的话,bbs 的时间就会停止,比方 web 登录就可能
511        会提示登录过于频繁。
512    int sysconfimg_version;
513        当前最新 sysconf 版本号,新登录用户会读入 sysconf.img.版本号
514        映像文件作为菜单。站务在主菜单按 shift+~ 会将这个字段加 1,并从
515        sysconf.ini 和 menu.ini 生成新的 sysconf.img.版本号 映像文件。
516        注意 web 不会自动读取新的 sysconf,如果有涉及 web 的参数修改(比方
517        BLOG MYSQL 密码),必须重新启动 web。
518    int www_guest_count;
519        当前登录的 wwwguest 数目
520    unsigned int max_user;
521        系统曾经到过的最高登录人数(包括 wwwguest)
522    unsigned int max_wwwguest;
523        系统到最高登录人数时,wwwguest 登录的数量。
524        注意这不是系统曾经到过的最高 wwwguest 登录人数。
525
526    /* etnlegend, 2006.03.06, userscore twice sampling for high score users ... */
527    unsigned int us_sample_high[8];
528
529    unsigned int logincount;
530    unsigned int logoutcount;
531    uint64_t staytime;
532    unsigned int wwwlogincount;
533    unsigned int wwwlogoutcount;
534    unsigned int wwwguestlogincount;
535    unsigned int wwwguestlogoutcount;
536    uint64_t wwwstaytime;
537    uint64_t wwwgueststaytime;
538
539    /* etnlegend, 2006.03.06, userscore sampling ... */
540    unsigned int us_sample[32];
541
542    /* etnlegend, 2006.05.28, 阅读十大 ... */
543    unsigned int top_version;
544    struct top_header{
545        int bid;
546        unsigned int gid;
547    } top[10];
548
549    char unused[712];
550   
551#ifdef FLOWBANNER
552    int bannercount;
553    char banners[MAXBANNER][BANNERSIZE];
554#endif
555   
556#ifdef FB2KENDLINE
557    time_t nextfreshdatetime;
558    char date[60];
559#endif
560};
561
5624.2 uidshm 用户信息共享内存结构 UCACHE
563
564uidshm 的结构 UCACHE 定义在 ucache.c 内,相关 hash 表常量在 uhashgen.h。
565
566struct UCACHE {
567    ucache_hashtable hashtable;
568    ucache_hashtable hashusage;
569    int hashhead[UCACHE_HASHSIZE + 1];
570    int next[MAXUSERS];
571        前面四个字段和用户 hash 表相关,后面一节具体说明
572    time_t uptime;
573        这个好像没用
574    int number;
575        有效用户容量,一般应该等于 MAXUSERS
576    char user_title[255][USER_TITLE_LEN]; //定义用户的身份字符串。
577        用户身份,user_title[0] 是 title 为 1 的用户的身份
578        信息和 $BBSHOME/etc/title 文件同步
579    struct userec passwd[MAXUSERS];
580        这是 .PASSWDS 文件的内容,定时和磁盘文件同步。
581        一个特定用户的用户号(uid)就是该用户在 .PASSWDS 文件中的位置,
582        注意 uid 是 1-based。
583};
584
5854.2.1 UCACHE 内 hash 表结构
586
587参考 doc/userid 文档。每个用户的用户名都有一个 hash 值(1 ~ UCACHE_HASHSIZE 之间),后面简称用户 hash 值。hashhead 数组是 hash 表头,存放的是该 hash 值的第一个用户的 uid。如果多个用户的 hash 值相同则用 next 字段的数据构成一条链,链中下一个用户的用户号是 next[uid - 1],如果该值为 0 表示已经到链尾。例如,系统中 hash 值都是 h 的用户一共有三个,uid 分别为 uid_1 uid_2 uid_3,那么 hash 表里可能会有这样的结构:
588uidshm->hashhead[h] = uid_1
589uidshm->next[uid_1 - 1] = uid_2
590uidshm->next[uid_2 - 1] = uid_3
591uidshm->next[uid_3 - 1] = 0
592
593另外,hashhead[0] 存储第一个空用户的 uid,便于下次分配新用户,所有的空用户 hash 值都是 0,所以也类似的通过 next 字段组成一条链。
594
595hashtable 和 hashusage 字段和 hash 函数相关,系统初始化时从 uhashgen.dat 读入。
596
597
5984.3 utmpshm 登录状态信息 UTMPFILE
599
600utmpshm 的类型是 UTMPFILE 结构,用来存储登录的状态信息。注意 wwwguest 和这个结构完全没有关系。
601
602#define USHM_SIZE       (MAXACTIVE + 10)
603struct UTMPFILE {
604    struct user_info uinfo[USHM_SIZE];
605        登录状态信息。每个登录都有一个登录号(utmpnum),他就是该登录在
606        uinfo 数组中的位置,注意 utmpnum 是 1-based。
607};
608
609uinfo 数组的每一个元素都可以用来存储一个登录的状态信息,其结构 user_info 定义:
610
611struct user_info {              /* Structure used in UTMP file */
612    int active;                 /* When allocated this field is true */
613        本结构当前是否代表一个登录用户。
614    int uid;                    /* Used to find user name in passwd file */
615        登录用户的 uid。
616    int pid;                    /* kill() to notify user of talk request */
617        telnet 登录表示其进程号。www 登录设置为 1。
618    int invisible;              /* Used by cloaking function in Xyz menu */
619        是否隐身。
620    int sockactive;             /* Used to coordinate talk requests */
621    int sockaddr;               /* ... */
622    int destuid;                /* talk uses this to identify who called */
623    int mode;                   /* UL/DL, Talk Mode, Chat Mode, ... */
624        状态,应该赋值为 modes.h 里面的常数。
625    int pager;                  /* pager toggle, true, or false */
626        呼叫器状态,bitwise-OR 以下属性
627            ALL_PAGER       0x1
628            FRIEND_PAGER    0x2
629            ALLMSG_PAGER    0x4
630            FRIENDMSG_PAGER 0x8
631    int in_chat;                /* for in_chat commands   */
632    char chatid[16];            /* chat id, if in chat mode */
633    char from[IPLEN + 4];       /* machine name the user called in from */
634        登录 IP。
635    time_t logintime;
636        登录时间戳。
637    int lastpost;
638        上次发文的时间戳。
639    char unused[32];
640    time_t freshtime;
641        上次活动的时间戳,用来计算发呆时间。
642    int utmpkey;
643        登录 key,用于 www cookie 验证保持用户身份。
644    unsigned int mailbox_prop;  /* properties of getCurrentUser()'s mailbox */
645        用户信箱选项,登录时从用户 .mailbox.prop 文件读取,参考 3.3 节
646    char userid[20];
647        用户名
648    char realname[20];
649        真实姓名,登录时从用户 userdata 结构读取
650    char username[40];
651        用户昵称,登录时从 uidshm 共享内存去读取,修改临时昵称就是修改这个字段
652    int friendsnum;
653        好友数量
654    int friends_uid[MAXFRIENDS];
655        每个好友的 uid,前 friendsnum 个有效。
656#ifdef FRIEND_MULTI_GROUP
657    unsigned int friends_p[MAXFRIENDS];
658#endif
659    int currentboard;
660        当前所在版面的 bid 号
661    unsigned int mailcheck;     /* if have new mail or new msg, stiger */
662        当前登录是否有新信或新消息
663};
664
665
6664.4 utmphead 登录表共享内存结构 UTMPHEAD
667
668utmphead 的结构是 UTMPHEAD,该结构定义在 var.h 中。注意 wwwguest 和这个结构完全没有关系。
669
670#define UTMP_HASHSIZE  (USHM_SIZE*4)
671struct UTMPHEAD {
672    int next[USHM_SIZE];
673    int hashhead[UTMP_HASHSIZE + 1];
674    int number;
675        当前登录数,注意这个数字不包括 wwwguest 数量。
676    int listhead;
677    int list_prev[USHM_SIZE];   /* sorted list prev ptr */
678    int list_next[USHM_SIZE];   /* sorted list next ptr */
679    time_t uptime;
680        一个标记时间,登录 telnet 新用户时如果当前时间和这个标记时间相差
681        120 秒以上,就遍历所有登录踢掉发呆时间过长的登录,同时将 uptime
682        设置成当前时间。
683};
684
685utmphead 里面也有一个 hash 表,结构类似于 uidshm 里面的 hash 表。这里 hash 值是从该登录用户的 hash 值计算(参见 utmp_hash() 函数),我们把它称为 utmphash 值(1 ~ UTMP_HASHSIZE 之间)。hashhead[h] 存储的就是第一个 utmphash 值为 h 的那个登录的 utmpnum。如果多个登录有相同的 utmphash 值则用 next 字段组成一条链(utmphash 链),具体来说,如果 utmpnum 是一个 active 登录,next[utmpnum - 1] 存储该登录在这条 utmphash 链中下一个登录的 utmpnum,链尾的 next[utmpnum - 1] == 0。
686
687所有没有登录(即 utmpshm->uinfo[utmpnum - 1].active = 0)的 utmphash 值都是 0,其中第一个 utmpnum 储存在 hashhead[0] 中,其余的通过 next 字段类似的组成一条链便于分配新登录。系统启动时没有用户登录,所以初始化为:
688hashhead[0] = 1
689next[0] = 2
690next[1] = 3
691... ...
692next[USHM_SIZE - 1] = 0
693
694utmphead 内除了 hash 表外还有一个头尾相接的环状双向链表,链表是所有 active 的登录按照登录用户名从小到大排列(case-insensitive)。listhead 是用户名最小的那个登录 utmpnum,list_prev[utmpnum - 1] 和 list_next[utmpnum - 1] 分别存储该链表中登录号为 utmpnum 的元素的前一个和后一个登录的 utmpnum。
695
6964.5 wwwguest_shm:wwwguest 在线用户表结构 WWW_GUEST_TABLE
697
698kbs 系统中的 wwwguest 登录处理是几乎完全独立的一部分,数据存储在 wwwguest_shm 共享内存区,结构为 WWW_GUEST_TABLE
699
700#define MAX_WWW_MAP_ITEM (MAX_WWW_GUEST/32)
701
702struct WWW_GUEST_TABLE {
703    int hashtab[16][256][256];
704    int use_map[MAX_WWW_MAP_ITEM + 1];
705        use_map[i] 的从低到高第 j 个 bit 表示当前 guest_entry[i * 32 + j] 是否被使用
706    time_t uptime;
707        一个标记时间,wwwguest 新登录时如果当前时间和这个标记时间相差
708        240 秒以上,就遍历所有 wwwguest 登录踢掉发呆时间过长的登录,同时将
709        uptime 设置成当前时间。
710    struct WWW_GUEST_S guest_entry[MAX_WWW_GUEST];
711        每个 wwwguest 登录的具体信息,结构定义如下
712};
713
714struct WWW_GUEST_S {
715    int key;
716    time_t freshtime;
717        上次活动的时间戳,用来计算发呆时间。
718    time_t logintime;
719    int currentboard;
720        当前所在版面的 bid
721    struct in_addr fromip;
722        来源 IP
723};
724
725
7264.6 bcache,brdshm:版面状态共享内存结构 BCACHE
727
728全局变量 bcache 指向 .BOARDS 文件的 mmap 数据。bcache[bid - 1] 是版面号为 bid 的那个版面的 boardheader
729
730如此之外,每个版面还有一些状态信息,即全局变量 brdshm 指向的共享内存(标识 BCACHE_SHMKEY),其结构 BCACHE 为:
731
732struct BCACHE {
733    int numboards;
734        所有版面版面号中最大的那个 bid。主要是遍历等工作可以只循环到 numboards
735        为止而无须到 MAXBOARD,以提高效率。
736    struct BoardStatus bstatus[MAXBOARD];
737        具体版面状态数据
738};
739
740其中 BoardStatus 的定义是:
741
742struct BoardStatus {            /* use this to speed up board list */
743    int total;
744        总文章数。如果版面文章数显示和实际不符就是因为这个字段没同步。
745    int lastpost;
746        本版最后一篇文章的 id 号,用户看到版面是否有新文章就是从这个字段计算。
747    bool updatemark;
748        是否被 m 的文章有变动,决定下次用户看被 m 文章列表是否需要重新产生索引
749    bool updatetitle;
750        是否置底文章有变动,决定下次使用 .DINGDIR 时是否需要重新产生
751    bool updateorigin;
752        是否原作文章有变动,决定下次使用 .ORIGIN 时是否需要重新产生
753    int currentusers;
754        当前该版面在线用户数,包括 wwwguest。
755    int nowid;
756        该版面文章当前已经使用到的 id 号,注意这个字段不一定等于 lastpost,比如
757        有人发表了两片文章 id=79,80,然后删掉 id=80 的文章,这时候 lastpost=79,
758        但是 nowid=80。在刷新 .BOARDS 时,该字段写回 .BOARDS 文件相应版面的
759        idseq 字段以便下次启动时读入。发表帖子的时候系统会自动根据 nowid 分配下
760        一个 id 值。如果这个字段出错,web 下浏览会不正常。
761    int toptitle;
762        本版面置顶文章个数
763    struct fileheader topfh[MAX_DING];
764        .DINGDIR 的内容,注意 MAX_DING 默认是 10,所以 .DINGDIR 内超过 10 条的
765        置顶只会被读入 10 条,更多的显示不出来。
766#ifdef HAVE_WFORUM
767    int todaynum;
768        这个字段没用,呵呵
769#endif
770};
771
772
7735. 代码结构
774
775kbs 系统的核心代码按照目录解释如下:
776libsystem/  系统最底层的函数库,以下所有程序都基于它
777libBBS/     一些核心函数组成的库 libBBS.so
778src/        telnet 服务器程序,静态连接 libBBS.so
779sshbbsd/    ssh 服务器程序,其实就是 src/ 下的程序套了一个 ssh 的壳
780daemon/     系统守护进程,应该启动服务时最先启动,静态连接 libBBS.so
781php/        输出供 PHP 页面使用的函数,动态连接 libBBS.so
782local_utl/  各类小实用程序,动态连接 libBBS.so
783
Note: See TracBrowser for help on using the browser.