| 1 | $Id$ |
|---|
| 2 | |
|---|
| 3 | 发信人: atppp |
|---|
| 4 | 标 题: [kbsIntro] kbs 系统入门 |
|---|
| 5 | 发信站: http://dev.kcn.cn/ |
|---|
| 6 | |
|---|
| 7 | 0. 序 |
|---|
| 8 | |
|---|
| 9 | 0.0 声明 |
|---|
| 10 | |
|---|
| 11 | 尽管 kbs 系统基础数据结构一般很少变化,但是本文档只针对 kbs SVN 主分支目前的代码,不保证对其它时间或其它分支代码的正确性,也不对可能造成的任何后果负责。本文可以在网络上自由转载,但请保留全文的完整性。 |
|---|
| 12 | |
|---|
| 13 | 0.1 本文说明 |
|---|
| 14 | |
|---|
| 15 | 文中有一些对数据结构的说明。大多数结构定义在 src/struct.h 中,char[] 的字段若非特别说明都是以 '\0' 结束的字符串。对本文或相关问题的疑问,欢迎到水木社区 (newsmth.net) BBSMan_Dev 版讨论。 |
|---|
| 16 | |
|---|
| 17 | |
|---|
| 18 | |
|---|
| 19 | 1. 版面及文章 |
|---|
| 20 | |
|---|
| 21 | 1.1 文章的存储 |
|---|
| 22 | |
|---|
| 23 | 每个版面都是一个目录。比如 SYSOP 版的目录是 $BBSHOME/boards/SYSOP/。在这个目录下面有这个版面的文章及索引,每一篇文章都是一个文件,文件名大致是这样子: |
|---|
| 24 | |
|---|
| 25 | M.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 | |
|---|
| 42 | 1.2 版面文章索引,fileheader 结构 |
|---|
| 43 | |
|---|
| 44 | 每个索引文件就是多个 fileheader 结构。这个 fileheader 定义在 src/default.h 或者 src/site.h 里面,具体解释如下: |
|---|
| 45 | |
|---|
| 46 | typedef 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 | |
|---|
| 99 | 1.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 | |
|---|
| 118 | 1.4 附件结构 |
|---|
| 119 | |
|---|
| 120 | 附件的内容就添加在它所属帖子的那个文件的末尾。有附件的帖子文件其组成是:帖子正文(最后是一个回车符),附件一,附件二,...其中每个附件段是由四个部分组成的: |
|---|
| 121 | *) 第一部分:八个 '\0' 字节。 |
|---|
| 122 | *) 第二部分:附件的原始文件名字符串,可以含有中文字符,长度不应该超过 40 个字 |
|---|
| 123 | 符。文件名的后缀将决定附件类型。本部分长度不定,所以千万不要忘记最后的 '\0' |
|---|
| 124 | 字符串结束符。 |
|---|
| 125 | *) 第三部分:四个字节,unsigned int 二进制格式整数,表示本附件的长度; |
|---|
| 126 | 注意这个整数是网络字节序(big-endian)存储,也即 MSB 在先。 |
|---|
| 127 | *) 第四部分:二进制格式储存的这个附件,本部分长度由第三部分决定。 |
|---|
| 128 | 另外,这个帖子在 .DIR 里面相应的那个 fileheader 结构的 attachment 字段应该设置为第一个附件段的起始偏移量(ftell)。参考: |
|---|
| 129 | libBBS/article.c upload_post_append() 和 get_mimetype() 函数 |
|---|
| 130 | |
|---|
| 131 | |
|---|
| 132 | 1.5 .BOARDS 文件,boardheader 结构 |
|---|
| 133 | |
|---|
| 134 | $BBSHOME/.BOARDS 文件是所有版面的信息,实际上是 MAXBOARD 个 boardheader 结构。在 kbs 系统内部每个版面都有一个版面号 bid,这个 bid 就是该版面在 .BOARDS 里面的位置,注意版面号是 1-based 的。 |
|---|
| 135 | |
|---|
| 136 | struct 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 | |
|---|
| 181 | 2. 用户 |
|---|
| 182 | |
|---|
| 183 | 2.1 什么样的 ID 在 kbs 系统中是合法的 |
|---|
| 184 | |
|---|
| 185 | kbs 系统合法 ID 的规则是:至少 2 个字符,至多 12 个字符。第一个字符必须是字母,后面的字符必须是字母或者数字。 |
|---|
| 186 | |
|---|
| 187 | |
|---|
| 188 | 2.2 用户相关的文件系统结构 |
|---|
| 189 | |
|---|
| 190 | $BBSHOME/.PASSWDS 是用户帐号基本信息,实质上是 MAXUSERS 个 userec 结构,结构说明后面详细写。另外,每个用户还有一个 home 目录和一个信件目录,比方 atppp 用户这两个目录分别是 $BBSHOME/home/A/atppp/ 和 $BBSHOME/mail/A/atppp/。用户 home 目录下有一些杂七杂八的文件,其中有一个文件叫做 .userdata,这个是用户帐号的补充信息,实际上就是一个 userdata 结构,结构说明后面详细写。信件目录到后面一大章节再说。 |
|---|
| 191 | |
|---|
| 192 | |
|---|
| 193 | 2.3 userec 结构 |
|---|
| 194 | |
|---|
| 195 | struct 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 | |
|---|
| 258 | 2.4 userdata 结构 |
|---|
| 259 | |
|---|
| 260 | userdata 是用户 home 目录下 .userdata 文件的结构。另外用户 home 目录下还有一个文件是 usermemo(用来 mmap 的),这个文件的内容要和 .userdata 严格一致,如果 usermemo 文件不存在系统会自动从 .userdata 建立,所以如果 usermemo 存在且和 .userdata 不一致的话系统就会出错。 |
|---|
| 261 | |
|---|
| 262 | userdata 结构: |
|---|
| 263 | |
|---|
| 264 | struct 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 | |
|---|
| 330 | 2.5 密码 |
|---|
| 331 | |
|---|
| 332 | kbs 的用户密码使用 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 | |
|---|
| 339 | 2.6 用户 home 目录下面其它一些文件的说明 |
|---|
| 340 | |
|---|
| 341 | 2.6.1 .boardrc.gz 已读记录 |
|---|
| 342 | |
|---|
| 343 | 用户 home 目录下面的 .boardrc.gz 存储用户的已读记录,它是一个使用 gzip 压缩的文件,解压后的长度是 |
|---|
| 344 | BRC_FILESIZE = MAXBOARD * BRC_MAXNUM * sizeof(unsigned int)。 |
|---|
| 345 | BRC_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 | |
|---|
| 351 | 2.6.2 favboard 收藏版面 |
|---|
| 352 | |
|---|
| 353 | 用户自定义了收藏版面之后会在用户 home 目录下创建文件 favboard。该文件的格式可以参考 libBBS/boards.c load_myboard1() save_favboard1() 函数。favboard 文件有多种允许的格式,而且 kbs 支持复杂的多目录层次收藏夹结构。下面只说明其中一种格式。favboard 文件可以是这样一个数据结构: |
|---|
| 354 | |
|---|
| 355 | struct { |
|---|
| 356 | int magic_version_number; |
|---|
| 357 | 写 0x8081 |
|---|
| 358 | int favbrd_list_t; |
|---|
| 359 | 收藏目录个数,写 1 |
|---|
| 360 | struct favbrd_struct fav_boards; |
|---|
| 361 | 具体的收藏版面 |
|---|
| 362 | }; |
|---|
| 363 | |
|---|
| 364 | favbrd_struct 具体的数据结构如下: |
|---|
| 365 | |
|---|
| 366 | struct 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 | |
|---|
| 385 | 2.6.3 friends 好友列表 |
|---|
| 386 | |
|---|
| 387 | 这是 n 个 friends 结构的文件,每个结构都是一个好友: |
|---|
| 388 | |
|---|
| 389 | struct friends { |
|---|
| 390 | char id[13]; |
|---|
| 391 | 好友 id |
|---|
| 392 | char exp[LEN_FRIEND_EXP]; |
|---|
| 393 | 好友说明,可以留空。 |
|---|
| 394 | }; |
|---|
| 395 | |
|---|
| 396 | 2.6.4 plans 个人说明档 |
|---|
| 397 | |
|---|
| 398 | 这个没什么好说的,就是个人说明档。查询用户的时候会显示出来。 |
|---|
| 399 | |
|---|
| 400 | 2.6.5 signatures 签名档 |
|---|
| 401 | |
|---|
| 402 | 这个文件是用户签名档,每六行是一个单位,支持 ansi 控制符,wForum 额外支持少量 ubb。userdata 结构的 signum 字段存储用户签名档个数,如果出现错误可以用recalc_signum 程序来纠正。 |
|---|
| 403 | |
|---|
| 404 | |
|---|
| 405 | |
|---|
| 406 | 3. 用户站内信件 |
|---|
| 407 | |
|---|
| 408 | 用户信件,包括信件索引和具体信件内容,全部位于用户信件目录下(参考 2.2 节)。 |
|---|
| 409 | |
|---|
| 410 | 3.1 信件目录及数据结构 |
|---|
| 411 | |
|---|
| 412 | 用户信件的总体构架基本类似于讨论区文章。每封信件都是一个文件,文件名的规则和讨论区普通文章的文件名相同。索引文件除了 .DIR 还有两个,如下: |
|---|
| 413 | |
|---|
| 414 | .DIR 收件箱 |
|---|
| 415 | .SENT 发件箱 |
|---|
| 416 | .DELETED 垃圾箱 |
|---|
| 417 | |
|---|
| 418 | 这三个索引文件的结构和讨论区文章索引 .DIR 的结构很类似,也是 n 个 fileheader 结构,少数几个字段的意义略有不同,具体解释如下: |
|---|
| 419 | |
|---|
| 420 | typedef 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 | |
|---|
| 451 | 3.2 自定义信箱 |
|---|
| 452 | |
|---|
| 453 | kbs 系统除了上面提到的三个预定义信箱外,还支持用户自定义信箱。载入自定义信箱的代码可以参考 libBBS/record.c load_mail_list() 函数。具体来说,在用户 home 目录下有一个 maildir 文件是自定义信箱的记录,文件结构是: |
|---|
| 454 | struct { |
|---|
| 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 | |
|---|
| 465 | 3.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 | |
|---|
| 479 | 4. 共享内存结构 |
|---|
| 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 | ====================================================================== |
|---|
| 490 | bcache .BOARDS 的 mmap bcache.c 只是一个 mmap |
|---|
| 491 | brdshm 版面状态 bcache.c BCACHE_SHMKEY 0xe6d |
|---|
| 492 | uidshm 用户信息 ucache.c UCACHE_SHMKEY 0xe70 |
|---|
| 493 | utmpshm 登录用户状态信息 utmp.c UTMP_SHMKEY 0xe73 |
|---|
| 494 | utmphead 登录表 utmp.c UTMPHEAD_SHMKEY 0xe72 |
|---|
| 495 | wwwguest_shm wwwguest 登录信息 bbslib.c WWWGUEST_SHMKEY 0x1194 |
|---|
| 496 | publicshm 全局信息 stuff.c 0xe74 (*) |
|---|
| 497 | |
|---|
| 498 | (*) 援引 bbs.h 注释: |
|---|
| 499 | #define PUBLIC_SHMKEY 3700 |
|---|
| 500 | /*这个是唯一一个定义死的SHMKEY,因为sysconf_eval需要 |
|---|
| 501 | public shm,而attach shm又需要sysconf_eval,ft*/ |
|---|
| 502 | |
|---|
| 503 | |
|---|
| 504 | 4.1 publicshm 共享内存结构 public_data |
|---|
| 505 | |
|---|
| 506 | struct 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 | |
|---|
| 562 | 4.2 uidshm 用户信息共享内存结构 UCACHE |
|---|
| 563 | |
|---|
| 564 | uidshm 的结构 UCACHE 定义在 ucache.c 内,相关 hash 表常量在 uhashgen.h。 |
|---|
| 565 | |
|---|
| 566 | struct 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 | |
|---|
| 585 | 4.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 表里可能会有这样的结构: |
|---|
| 588 | uidshm->hashhead[h] = uid_1 |
|---|
| 589 | uidshm->next[uid_1 - 1] = uid_2 |
|---|
| 590 | uidshm->next[uid_2 - 1] = uid_3 |
|---|
| 591 | uidshm->next[uid_3 - 1] = 0 |
|---|
| 592 | |
|---|
| 593 | 另外,hashhead[0] 存储第一个空用户的 uid,便于下次分配新用户,所有的空用户 hash 值都是 0,所以也类似的通过 next 字段组成一条链。 |
|---|
| 594 | |
|---|
| 595 | hashtable 和 hashusage 字段和 hash 函数相关,系统初始化时从 uhashgen.dat 读入。 |
|---|
| 596 | |
|---|
| 597 | |
|---|
| 598 | 4.3 utmpshm 登录状态信息 UTMPFILE |
|---|
| 599 | |
|---|
| 600 | utmpshm 的类型是 UTMPFILE 结构,用来存储登录的状态信息。注意 wwwguest 和这个结构完全没有关系。 |
|---|
| 601 | |
|---|
| 602 | #define USHM_SIZE (MAXACTIVE + 10) |
|---|
| 603 | struct UTMPFILE { |
|---|
| 604 | struct user_info uinfo[USHM_SIZE]; |
|---|
| 605 | 登录状态信息。每个登录都有一个登录号(utmpnum),他就是该登录在 |
|---|
| 606 | uinfo 数组中的位置,注意 utmpnum 是 1-based。 |
|---|
| 607 | }; |
|---|
| 608 | |
|---|
| 609 | uinfo 数组的每一个元素都可以用来存储一个登录的状态信息,其结构 user_info 定义: |
|---|
| 610 | |
|---|
| 611 | struct 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 | |
|---|
| 666 | 4.4 utmphead 登录表共享内存结构 UTMPHEAD |
|---|
| 667 | |
|---|
| 668 | utmphead 的结构是 UTMPHEAD,该结构定义在 var.h 中。注意 wwwguest 和这个结构完全没有关系。 |
|---|
| 669 | |
|---|
| 670 | #define UTMP_HASHSIZE (USHM_SIZE*4) |
|---|
| 671 | struct 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 | |
|---|
| 685 | utmphead 里面也有一个 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 字段类似的组成一条链便于分配新登录。系统启动时没有用户登录,所以初始化为: |
|---|
| 688 | hashhead[0] = 1 |
|---|
| 689 | next[0] = 2 |
|---|
| 690 | next[1] = 3 |
|---|
| 691 | ... ... |
|---|
| 692 | next[USHM_SIZE - 1] = 0 |
|---|
| 693 | |
|---|
| 694 | utmphead 内除了 hash 表外还有一个头尾相接的环状双向链表,链表是所有 active 的登录按照登录用户名从小到大排列(case-insensitive)。listhead 是用户名最小的那个登录 utmpnum,list_prev[utmpnum - 1] 和 list_next[utmpnum - 1] 分别存储该链表中登录号为 utmpnum 的元素的前一个和后一个登录的 utmpnum。 |
|---|
| 695 | |
|---|
| 696 | 4.5 wwwguest_shm:wwwguest 在线用户表结构 WWW_GUEST_TABLE |
|---|
| 697 | |
|---|
| 698 | kbs 系统中的 wwwguest 登录处理是几乎完全独立的一部分,数据存储在 wwwguest_shm 共享内存区,结构为 WWW_GUEST_TABLE |
|---|
| 699 | |
|---|
| 700 | #define MAX_WWW_MAP_ITEM (MAX_WWW_GUEST/32) |
|---|
| 701 | |
|---|
| 702 | struct 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 | |
|---|
| 714 | struct 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 | |
|---|
| 726 | 4.6 bcache,brdshm:版面状态共享内存结构 BCACHE |
|---|
| 727 | |
|---|
| 728 | 全局变量 bcache 指向 .BOARDS 文件的 mmap 数据。bcache[bid - 1] 是版面号为 bid 的那个版面的 boardheader |
|---|
| 729 | |
|---|
| 730 | 如此之外,每个版面还有一些状态信息,即全局变量 brdshm 指向的共享内存(标识 BCACHE_SHMKEY),其结构 BCACHE 为: |
|---|
| 731 | |
|---|
| 732 | struct BCACHE { |
|---|
| 733 | int numboards; |
|---|
| 734 | 所有版面版面号中最大的那个 bid。主要是遍历等工作可以只循环到 numboards |
|---|
| 735 | 为止而无须到 MAXBOARD,以提高效率。 |
|---|
| 736 | struct BoardStatus bstatus[MAXBOARD]; |
|---|
| 737 | 具体版面状态数据 |
|---|
| 738 | }; |
|---|
| 739 | |
|---|
| 740 | 其中 BoardStatus 的定义是: |
|---|
| 741 | |
|---|
| 742 | struct 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 | |
|---|
| 773 | 5. 代码结构 |
|---|
| 774 | |
|---|
| 775 | kbs 系统的核心代码按照目录解释如下: |
|---|
| 776 | libsystem/ 系统最底层的函数库,以下所有程序都基于它 |
|---|
| 777 | libBBS/ 一些核心函数组成的库 libBBS.so |
|---|
| 778 | src/ telnet 服务器程序,静态连接 libBBS.so |
|---|
| 779 | sshbbsd/ ssh 服务器程序,其实就是 src/ 下的程序套了一个 ssh 的壳 |
|---|
| 780 | daemon/ 系统守护进程,应该启动服务时最先启动,静态连接 libBBS.so |
|---|
| 781 | php/ 输出供 PHP 页面使用的函数,动态连接 libBBS.so |
|---|
| 782 | local_utl/ 各类小实用程序,动态连接 libBBS.so |
|---|
| 783 | |
|---|