Coder Social home page Coder Social logo

Comments (2)

IepIweidieng avatar IepIweidieng commented on August 11, 2024

實作登入次數自動增加機制的程式碼,根據以 PttBBS 原始碼為範圍、使用 ripgrep 所取得的字串搜尋結果,推測有兩處:

  • mbbsd/passwd.c pwcuLoginSave()——每日登入時增加一次(「登入時的次數增加」)
    • 次數增加條件:上一次增加登入次數的時間(cuser.lastlogin)在本次登入日 00:00 之前
  • util/update_online.c fastcheck()——單次線上期間每超過一天增加一次(「線上期間的次數增加」)
    • 次數增加條件:每天某時間點時,使用者在線上且距離上一次增加登入次數的時間超過一天。這個時間會依站臺設定而異,在 Ptt 上是 04:00。

上述兩者在增加登入次數(cuser.numlogindays)時,都會設定 cuser.lastlogin 為某個接近目前時間的時間值(「重設用目前時間值」)。

節錄自 util/update_online.c 的相關程式碼

time4_t base;
base = time4(0) - DAY_SECONDS;

if (urec.lastlogin >= base)
continue;
if (verbose)
fprintf(stderr, "update: %s (%s, %d) ->", urec.userid,
Cdatelite(&urec.lastlogin), urec.numlogindays);
/* user still online, let's mock it. */
urec.lastlogin = now;
urec.numlogindays++;
if (verbose)
fprintf(stderr, "(%s, %d).\n", Cdatelite(&urec.lastlogin), urec.numlogindays);
passwd_update(last_uid, &urec);

now = time(NULL);

線上使用者登入後,其線上期間的計算方式為 now_cond - cuser.lastlogin,其中 now_cond 為「判斷用目前時間值」,而 cuser 為對應此使用者的 userec_t 物件。

以下程式碼會阻止線上期間未超過一天的使用者的登入次數增加:

if (urec.lastlogin >= base)
continue;

urec.lastlogin >= base(1)
urec.lastlogin >= now_cond - DAY_SECONDS (now_cond 為「判斷用目前時間值」)
urec.lastlogin + DAY_SECONDS >= now_cond
now_cond <= urec.lastlogin + DAY_SECONDS(2)
now_cond - urec.lastlogin <= DAY_SECONDS
duration <= DAY_SECONDS (duration 為線上期間)

可知在 update_online 所算出的線上期間大於一天的情況下,才會增加該使用者的登入次數。

作為比較,這是 commit 05702e5 時的對應程式碼:

if (now < urec.lastlogin + DAY_SECONDS - 60 * 60)
continue;

上述 (2) 式與之相比多了等號。

根據前面討論所提到的「線上期間的次數增加」機制的描述,以及 update_online 是支單獨程式的事實,我推測 update_online 是透過 cron 每天定時執行一次的。

假設電腦執行速度快得使「重設用目前時間值」(在上述節錄內容內,行號為 77 之行)與「判斷用目前時間值」(在上述節錄內容內,行號為 16 之行)皆相同。(因為參考 util/update_online.c 與其所使用的 common/bbs/cache.c 中的函式的定義,在程式執行流程中的上述兩行程式碼間不存在固定的延時,所以如果電腦執行速度相當快的話,這兩行可視為同時執行)

情況一

如果 cron 某次執行 update_online 時延遲,導致所取得的「重設用目前時間值」比預期延後超過一秒,這個情況下如果下次執行時所取得的時間值與預期相比沒有延後,那麼本次登入後已被 update_online 增加過登入次數且仍在線上的使用者,將會因為所算出的線上期間小於一天,所以其登入次數不會增加,造成少算的狀況。

一種解決方法是重新引入時間判斷的誤差容許值,但在定時執行未出現延遲時會導致允許將線上期間未超過一天的使用者的登入次數增加。

另一種解決方法是將「重設用目前時間值」與「判斷用目前時間值」取為最近的分鐘或小時的開始時間。

又另一種解決方法請見情況四。


假設電腦執行速度快,cron 每天皆一秒不差地定時執行一次 update_online,使得其執行時間的值、「重設用目前時間值」、與「判斷用目前時間值」皆相同。

情況二

如果使用者被過去最近一次 update_online 的每天定時執行增加了登入次數,或如果使用者本次登入後未被 update_online 增加過登入次數,並且使用者登入的時間點為過去最近一次 update_online 執行時所取得的「判斷用目前時間值」的時間點,那麼下一次執行時,這個使用者的線上期間將會等於一天,所以其登入次數不會增加,造成少算的狀況。遇到下二次執行時才會增加,但僅增加一次。

修正方法是移除上述 (1) 式中的等號。

情況三

如果使用者本次登入後未被 update_online 增加過登入次數,並且使用者登入的時間點比過去最近一次 update_online 執行時所取得的「判斷用目前時間值」的時間點還晚超過一秒後登入,那麼這個使用者的線上期間達到大於等於一天的時間會落在下一次到下二次遇到 update_online 每天定時執行之間。這個情況下,如果這個使用者在下二次遇到 update_online 的每天定時執行前離線,將會因為 update_online 不處理離線使用者而不增加其登入次數而造成少算的狀況。

一種解決方法是在使用者登出時會執行的處理函式(例如 pwcuExitSave() )中,加上以下處理邏輯:

如果以 cuser.lastlogin - now 算出的線上期間大於等於一天則增加一次登入次數(「離線時的次數增加」)。

但在使用者的對應處理程序異常終止執行而導致「離線時的次數增加」機制未被執行的情況下,還是會造成少算的狀況。

情況四

以下是虛構的說明範例:

  • 使用者甲在某天 03:59:59 登入,在後天 04:00:01 登出,在線上共 48 時 0 分 2 秒
    • 登入次數增加時間點:第一天 03:59:59、第二天 04:00:00、第三天 04:00:00,共三次
  • 使用者乙在某天 04:00:01 登入,在後天 04:00:03 登出,在線上共 48 時 0 分 2 秒
    • 登入次數增加時間點:第一天 04:00:01、第三天 04:00:00,共兩次

可見使用者甲與乙的線上期間相同,僅有登入與登出的時間有些許前後差別,但乙的登入次數卻少算了一次。

一種解決方法是將「重設用目前時間值」取為 cuser.lastlogin + DAY_SECONDS 以保留實際線上期間的資訊,並搭配使用情況三的解決方法處理離線時的線上期間。

使用此解決方法後的上述說明範例:

  • 使用者甲在某天 03:59:59 登入,在後天 04:00:01 登出,在線上共 48 時 0 分 2 秒
    • 登入次數增加時間點:第一天 03:59:59(登入)、第二天 04:00:00(線上;尚有 1 秒未計)、第三天 04:00:00(線上;尚有 1 秒未計),共三次
  • 使用者乙在某天 04:00:01 登入,在後天 04:00:03 登出,在線上共 48 時 0 分 2 秒
    • 登入次數增加時間點:第一天 04:00:01(登入)、第三天 04:00:00(線上;尚有 23 時 59 分 59 秒未計)、第三天 04:00:03(登出;尚有 2 秒未計),共三次

from pttbbs.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.