Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
fce58c0
Prevent unexpected write to articles pointed by p_articles[]
leafok88 Dec 3, 2025
4cb88c3
Update logrotate config
leafok88 Dec 4, 2025
b163047
Ignore SIGPIPE
leafok88 Dec 16, 2025
e0d0cfe
Add Copilot Instructions
leafok88 Dec 16, 2025
9daae30
Refine cleanup code for errors
leafok88 Dec 16, 2025
34f8ab5
Refine init / cleanup of iconv_cd
leafok88 Dec 16, 2025
9314380
Fix bug
leafok88 Dec 16, 2025
c3f22d1
Add error handling of fcntl()
leafok88 Dec 16, 2025
34adaff
Refine logging the count of concurrent connections
leafok88 Dec 16, 2025
d437434
Refine log
leafok88 Dec 16, 2025
5f88ed2
Refine log
leafok88 Dec 16, 2025
09bc03c
Refine handling of SSH_AGAIN during handshake of ssh connection
leafok88 Dec 16, 2025
264f780
Refine hash_dict: hash_dict_inc() no longer add non-existing key. It …
leafok88 Dec 17, 2025
3880e19
Apply different handling to first connection and concurrent connectio…
leafok88 Dec 17, 2025
4c9acf4
Refine debug log
leafok88 Dec 17, 2025
92b1b03
Refine debug log
leafok88 Dec 17, 2025
ebf110a
Fix bug
leafok88 Dec 17, 2025
e7c0823
Refine
leafok88 Dec 17, 2025
e554306
Replace deprecated functions
leafok88 Dec 18, 2025
5aec46d
Add handling of error events (EPOLLRDHUP | EPOLLHUP | EPOLLERR)
leafok88 Dec 18, 2025
67f2fb0
Add log_debug()
leafok88 Dec 18, 2025
80f7a1e
Refine with log_debug()
leafok88 Dec 18, 2025
e422587
Do not monitor EPOLLRDHUP / POLLRDHUP, since POLLRDHUP is not support…
leafok88 Dec 18, 2025
3296407
Refine and bug fix
leafok88 Dec 18, 2025
400b124
Update version to 1.6.3
leafok88 Dec 18, 2025
c8de5c8
Refine
leafok88 Dec 18, 2025
4b73ffd
Fix issue
leafok88 Dec 18, 2025
e30a899
Refine
leafok88 Dec 18, 2025
c37b421
Fix bug
leafok88 Dec 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# LBBS — Copilot Instructions

Short, specific pointers to help an AI coding agent be productive in this repo.

**Build & Test**
- **Install deps**: libssh, libpcre2 and a MySQL/MariaDB client dev package (e.g., libssh-dev, libpcre2-dev, libmariadb-dev or libmysqlclient-dev). CI also installs `libsystemd-dev`.
- **Build**: `autoreconf --install --force` then `./configure --enable-systemd --disable-silent-rules` and `make`.
- **Run tests**: `make check` (tests are custom binaries built under `src/`, e.g. `test_trie_dict`). CI runs `make check` and `make distcheck` (see [.github/workflows/makefile.yml](.github/workflows/makefile.yml#L1-L40)).

**Run & Debug**
- **Server binary**: `src/bbsd` (entry: [src/main.c](src/main.c#L1-L60)).
- **Run in foreground**: `src/bbsd -f` (use `--display-log`/`--display-error-log` to redirect logs to stderr).
- **Debug**: run with `gdb --args src/bbsd -f` or attach to the process; logs are in `log/`.

**High-level architecture**
- **Process**: `bbsd` (server) loads config and data files at start, initializes shared pools, listens for telnet/ssh clients and spawns handlers.
- **Networking**: accept loop and client lifecycle are in [src/net_server.c](src/net_server.c#L1-L140); user-facing shuttle/menu networking is in [src/bbs_net.c](src/bbs_net.c#L1-L60).
- **IO / Encoding**: terminal IO and non-blocking I/O are handled in [src/io.c](src/io.c#L1-L80). The project supports epoll/poll (conditional) and uses iconv for charset conversion. Default charset is `UTF-8`.
- **Auth & DB**: login and auth live in [src/login.c](src/login.c#L1-L40); database access is in [src/database.c](src/database.c#L1-L40). DB credentials are read from `conf/bbsd.conf` (example in [conf/bbsd.conf](conf/bbsd.conf#L1-L40)).

**Config & runtime files**
- **Configs**: `conf/` contains runtime config (e.g., `conf/bbsd.conf`, `conf/bbsnet.conf`). Config constants are centralized in [src/common.c](src/common.c#L1-L40) as `CONF_*`.
- **b b snet format**: each line is `ORG SITE HOST PORT USE_SSH(Y/N) CHARSET` (see how it's parsed in [src/bbs_net.c](src/bbs_net.c#L88-L118)).
- **SSH keys**: host keys live in `conf/` (see `SSH_HOST_*` constants in [src/common.c](src/common.c#L1-L40)).
- **Data & runtime**: static text files in `data/`; generated runtime files and caches in `var/`; logs in `log/`.

**Project patterns and conventions**
- **Autotools**: use `autoconf/automake` (`configure.ac`, `Makefile.am`); prefer `autoreconf` + `./configure` for local iterations.
- **Tests**: tests are small programs named `test_*` in `src/` and are exercised by `make check`. Add new tests as `test_<feature>.c` and register in `src/Makefile.am`.
- **Error/logging**: prefer `log_common()` / `log_error()` instead of printing to stdout. Most functions return negative on error.
- **Globals & constants**: global config names use prefixes like `BBS_`, `VAR_`, `CONF_` (see [src/common.c](src/common.c#L1-L80)).

**When changing networking/IO code**
- Run `make check` and manually run `src/bbsd -f` to exercise interactive code paths.
- For SSH-related changes, ensure `libssh` is present and try `test_ssh_server` in `src/`.
- For charset changes, check `io.c` and the per-connection iconv usage (see [src/bbs_net.c](src/bbs_net.c#L250-L320)).

**CI & packaging**
- CI uses Ubuntu runners, installs system packages and runs `./configure`, `make`, `make check`, `make distcheck` (see [.github/workflows/makefile.yml](.github/workflows/makefile.yml#L1-L80)).

If any of these sections look incomplete or you want more examples (e.g., walk-through for adding a test or adding a `configure` option), tell me which area to expand. I can iterate. (Drafted by AI; please confirm tone and level of detail.)
2 changes: 1 addition & 1 deletion conf/lbbs.logrotate.in
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
@prefix@/log/*.log {
daily
missingok
compress
rotate 90
delaycompress
notifempty
create 640 bbs bbs
Expand Down
2 changes: 1 addition & 1 deletion configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script.

AC_PREREQ([2.69])
AC_INIT([lbbs],[1.6.2])
AC_INIT([lbbs],[1.6.3])
AC_CONFIG_SRCDIR([src/])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_MACRO_DIRS([m4])
Expand Down
2 changes: 1 addition & 1 deletion include/article_favor.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ extern int article_favor_check(int32_t aid, const ARTICLE_FAVOR *p_favor);
// Set specific article as favorite
extern int article_favor_set(int32_t aid, ARTICLE_FAVOR *p_favor, int state);

extern int query_favor_articles(ARTICLE_FAVOR *p_favor, int page_id, ARTICLE **p_articles,
extern int query_favor_articles(ARTICLE_FAVOR *p_favor, int page_id, const ARTICLE **p_articles,
char p_snames[][BBS_section_name_max_len + 1], int *p_article_count, int *p_page_count);

#endif //_ARTICLE_FAVOR_H_
7 changes: 7 additions & 0 deletions include/hash_dict.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,16 @@ typedef struct hash_dict_t HASH_DICT;
extern HASH_DICT *hash_dict_create(int item_count_limit);
extern void hash_dict_destroy(HASH_DICT *p_dict);

// Return 1 if existing key updated, 0 if new key added, -1 on error
extern int hash_dict_set(HASH_DICT *p_dict, uint64_t key, int64_t value);

// Return 1 if existing key updated, 0 if key not exist, -1 on error
extern int hash_dict_inc(HASH_DICT *p_dict, uint64_t key, int64_t value_inc);

// Return 1 if key found, 0 if key not exist, -1 on error
extern int hash_dict_get(HASH_DICT *p_dict, uint64_t key, int64_t *p_value);

// Return 1 if key deleted, 0 if key not exist, -1 on error
extern int hash_dict_del(HASH_DICT *p_dict, uint64_t key);

inline unsigned int hash_dict_item_count(HASH_DICT *p_dict)
Expand Down
11 changes: 9 additions & 2 deletions include/log.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@

enum log_level_t
{
LOG_LEVEL_COMMON = 1,
LOG_LEVEL_ERROR = 2,
LOG_LEVEL_COMMON,
LOG_LEVEL_ERROR,
LOG_LEVEL_DEBUG,
};

extern int log_begin(const char *common_log_file, const char *error_log_file);
Expand All @@ -25,6 +26,12 @@ extern int log_printf(enum log_level_t log_level, const char *app_file, int app_
#define log_common(...) log_printf(LOG_LEVEL_COMMON, __FILE__, __LINE__, __VA_ARGS__)
#define log_error(...) log_printf(LOG_LEVEL_ERROR, __FILE__, __LINE__, __VA_ARGS__)

#ifdef _DEBUG
#define log_debug(...) log_printf(LOG_LEVEL_DEBUG, __FILE__, __LINE__, __VA_ARGS__)
#else
#define log_debug(...) ((void)0)
#endif

extern int log_common_redir(int fd);
extern int log_error_redir(int fd);

Expand Down
2 changes: 1 addition & 1 deletion include/section_list_loader.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ extern int section_list_loader_launch(void);

// Return on success : real page_id (>= 0)
// failure : error number (< 0)
extern int query_section_articles(SECTION_LIST *p_section, int page_id, ARTICLE *p_articles[],
extern int query_section_articles(SECTION_LIST *p_section, int page_id, const ARTICLE *p_articles[],
int *p_article_count, int *p_page_count, int *p_ontop_start_offset);

// Input direction = 0 : locate p_article_cur
Expand Down
12 changes: 3 additions & 9 deletions src/article_cache.c
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,9 @@ int article_cache_generate(const char *cache_dir, const ARTICLE *p_article, cons

if (header_len != cache.line_offsets[header_line_cnt])
{
#ifdef _DEBUG
log_error("Header of article(aid=%d) is truncated from %ld to %ld\n, body_line=%ld, body_line_limit=%ld",
log_debug("Header of article(aid=%d) is truncated from %ld to %ld\n, body_line=%ld, body_line_limit=%ld",
p_article->aid, header_len, cache.line_offsets[header_line_cnt],
header_line_cnt, MAX_SPLIT_FILE_LINES);
#endif
header_len = (size_t)cache.line_offsets[header_line_cnt];
}

Expand All @@ -147,11 +145,9 @@ int article_cache_generate(const char *cache_dir, const ARTICLE *p_article, cons

if (body_len != (size_t)cache.line_offsets[cache.line_total])
{
#ifdef _DEBUG
log_error("Body of article(aid=%d) is truncated from %ld to %ld, body_line=%ld, body_line_limit=%ld\n",
log_debug("Body of article(aid=%d) is truncated from %ld to %ld, body_line=%ld, body_line_limit=%ld\n",
p_article->aid, body_len, cache.line_offsets[cache.line_total],
body_line_cnt, MAX_SPLIT_FILE_LINES - header_line_cnt);
#endif
cache.data_len = header_len + (size_t)(cache.line_offsets[cache.line_total]);
}

Expand All @@ -165,11 +161,9 @@ int article_cache_generate(const char *cache_dir, const ARTICLE *p_article, cons

if (footer_len != cache.line_offsets[cache.line_total + footer_line_cnt])
{
#ifdef _DEBUG
log_error("Footer of article(aid=%d) is truncated from %ld to %ld, footer_line=%ld, footer_line_limit=%ld\n",
log_debug("Footer of article(aid=%d) is truncated from %ld to %ld, footer_line=%ld, footer_line_limit=%ld\n",
p_article->aid, footer_len, cache.line_offsets[cache.line_total + footer_line_cnt],
footer_line_cnt, MAX_SPLIT_FILE_LINES - cache.line_total);
#endif
footer_len = (size_t)(cache.line_offsets[cache.line_total + footer_line_cnt]);
}

Expand Down
2 changes: 1 addition & 1 deletion src/article_favor.c
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,7 @@ int article_favor_set(int32_t aid, ARTICLE_FAVOR *p_favor, int state)
return 1; // Set complete
}

int query_favor_articles(ARTICLE_FAVOR *p_favor, int page_id, ARTICLE **p_articles,
int query_favor_articles(ARTICLE_FAVOR *p_favor, int page_id, const ARTICLE **p_articles,
char p_snames[][BBS_section_name_max_len + 1], int *p_article_count, int *p_page_count)
{
SECTION_LIST *p_section;
Expand Down
6 changes: 3 additions & 3 deletions src/article_favor_display.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ static int article_favor_draw_screen(int display_sname)
return 0;
}

static int article_favor_draw_items(int page_id, ARTICLE *p_articles[], char p_snames[][BBS_section_name_max_len + 1],
static int article_favor_draw_items(int page_id, const ARTICLE *p_articles[], char p_snames[][BBS_section_name_max_len + 1],
int article_count, int display_sname)
{
char str_time[LINE_BUFFER_LEN];
Expand Down Expand Up @@ -172,7 +172,7 @@ static enum select_cmd_t article_favor_select(int total_page, int item_count, in
switch (ch)
{
case KEY_NULL: // broken pipe
log_error("KEY_NULL\n");
log_debug("KEY_NULL\n");
case KEY_ESC:
case KEY_LEFT:
return EXIT_LIST; // exit list
Expand Down Expand Up @@ -299,7 +299,7 @@ int article_favor_display(ARTICLE_FAVOR *p_favor)

char page_info_str[LINE_BUFFER_LEN];
char snames[BBS_article_limit_per_page][BBS_section_name_max_len + 1];
ARTICLE *p_articles[BBS_article_limit_per_page];
const ARTICLE *p_articles[BBS_article_limit_per_page];
int article_count;
int page_count;
int page_id = 0;
Expand Down
6 changes: 2 additions & 4 deletions src/bbs_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -171,9 +171,7 @@ int bbs_center()
if (ch != KEY_NULL && ch != KEY_TIMEOUT)
{
BBS_last_access_tm = time(NULL);
#ifdef _DEBUG
log_error("Debug: BBS_last_access_tm is updated\n");
#endif
log_debug("Debug: BBS_last_access_tm is updated\n");
}

if (bbs_menu.choose_step == 0 && time(NULL) - t_last_action >= 10)
Expand All @@ -194,7 +192,7 @@ int bbs_center()
switch (ch)
{
case KEY_NULL: // broken pipe
log_error("KEY_NULL\n");
log_debug("KEY_NULL\n");
loop = 0;
break;
case KEY_TIMEOUT:
Expand Down
Loading