2017年8月3日 星期四
message queue in shell/bash
最近在寫些測試程式(Shell on busybox),發現有些動作需要到背景執行,但是一些執行結果要回傳給主程序,或是一些共用變數都很麻煩,背景的Shell (背景可能很多個,分測試不同項目) 和主程序是兩個獨力的Task ,要互通只有用IPC了,當然可以用檔案方式,但是感覺不是很優!
Shell 要使用 IPC ,有些麻煩了!還好Open Source 就有好處,google 了一些後發現下列網頁:
https://blog.garage-coding.com/2016/02/05/bash-fifo-jobqueue.html
其中使用Message queue 的方式比較適用我的狀況!
ipcmd 需要到GitHub 去下載Source code,編譯後就可以用了!
https://github.com/nathanweeks/ipcmd
整個機本原理就是用C 完成message queue 的動做(ipcmd) ,然後在 shell 中使用。
2016年7月11日 星期一
dmix of ALSA library.
最近工作上需要讓 不同的兩個 AP 都能透過 ALSA 同時撥 .WAV file, 也就是需要兩個來源混音.
Google 了一下, 發現 ALSA Lib 就有內建 dmix (direct mixing) 的功能.
http://www.alsa-project.org/main/index.php/Asoundrc
These days we have a native plugin for ALSA called the
也有很多網頁說明要填寫 /etc/asound.conf , 我按照網頁的填寫方式後用 aplay 去驗證發現下列的問題:
ALSA lib pcm_direct.c:812:(snd_pcm_direct_initialize_slave) requested or auto-format is not available
ALSA lib pcm_dmix.c:831:(snd_pcm_dmix_open) unable to initialize slave
追蹤 了一下 error code 的來源發生在下列片段內, 主要是沒有辦法設定格式,檢查了aousnd.conf 也看不出任何錯誤.
int snd_pcm_direct_initialize_slave(snd_pcm_direct_t *dmix, snd_pcm_t *spcm, struct slave_params *params)
{
........................................................
ret = snd_pcm_hw_params_set_format(spcm, hw_params, params->format);
if (ret < 0) {
static const snd_pcm_format_t dmix_formats[] = {
SND_PCM_FORMAT_S32,
SND_PCM_FORMAT_S32 ^ SND_PCM_FORMAT_S32_LE ^ SND_PCM_FORMAT_S32_BE,
SND_PCM_FORMAT_S16,
SND_PCM_FORMAT_S16 ^ SND_PCM_FORMAT_S16_LE ^ SND_PCM_FORMAT_S16_BE,
SND_PCM_FORMAT_S24_3LE,
};
snd_pcm_format_t format;
unsigned int i;
for (i = 0; i < sizeof dmix_formats / sizeof dmix_formats[0]; ++i) {
format = dmix_formats[i];
ret = snd_pcm_hw_params_set_format(spcm, hw_params, format);
if (ret >= 0)
break;
}
if (ret < 0 && dmix->type != SND_PCM_TYPE_DMIX) {
/* TODO: try to choose a good format */
ret = INTERNAL(snd_pcm_hw_params_set_format_first)(spcm, hw_params, &format);
}
if (ret < 0) {
SNDERR("requested or auto-format is not available");
return ret;
}
params->format = format;
................................
}
Google 上也沒有找到說明很清楚這樣的問題點出現在哪部份.
經過幾天的 trial and error 發現, 主要是我的 CODEC driver 並不支援dmix 所需要的 format.
dmix 只支援下列格式(由 code可以知道) .
SND_PCM_FORMAT_S32,
SND_PCM_FORMAT_S32 ^ SND_PCM_FORMAT_S32_LE ^ SND_PCM_FORMAT_S32_BE,
SND_PCM_FORMAT_S16,
SND_PCM_FORMAT_S16 ^ SND_PCM_FORMAT_S16_LE ^ SND_PCM_FORMAT_S16_BE,
SND_PCM_FORMAT_S24_3LE,
所以就先修改我的CODEC driver , 讓它支援 S16_LE (之前只有 支援 U16_LE ).
這個 bug 就解決了 , 並且 asound.conf 也可以讓 "default" 順利的轉向到 dmix 上.
所以 dmix 如果出現
ALSA lib pcm_direct.c:812:(snd_pcm_direct_initialize_slave) requested or auto-format is not available
錯誤 , 要先看看 codec driver 是否支援 dmix 需要的格式.
PS: 有關 asound.conf 可以參考下列網頁 , 我認為寫的比較清楚的.
http://forums.gentoo.tw/viewtopic.php?f=18&t=44507
Google 了一下, 發現 ALSA Lib 就有內建 dmix (direct mixing) 的功能.
http://www.alsa-project.org/main/index.php/Asoundrc
These days we have a native plugin for ALSA called the
dmix
(direct mixing) plugin. It allows software mixing in an easy to use
syntax and without the hassle of installing/understanding a new
application first.
也有很多網頁說明要填寫 /etc/asound.conf , 我按照網頁的填寫方式後用 aplay 去驗證發現下列的問題:
ALSA lib pcm_direct.c:812:(snd_pcm_direct_initialize_slave) requested or auto-format is not available
ALSA lib pcm_dmix.c:831:(snd_pcm_dmix_open) unable to initialize slave
追蹤 了一下 error code 的來源發生在下列片段內, 主要是沒有辦法設定格式,檢查了aousnd.conf 也看不出任何錯誤.
int snd_pcm_direct_initialize_slave(snd_pcm_direct_t *dmix, snd_pcm_t *spcm, struct slave_params *params)
{
........................................................
ret = snd_pcm_hw_params_set_format(spcm, hw_params, params->format);
if (ret < 0) {
static const snd_pcm_format_t dmix_formats[] = {
SND_PCM_FORMAT_S32,
SND_PCM_FORMAT_S32 ^ SND_PCM_FORMAT_S32_LE ^ SND_PCM_FORMAT_S32_BE,
SND_PCM_FORMAT_S16,
SND_PCM_FORMAT_S16 ^ SND_PCM_FORMAT_S16_LE ^ SND_PCM_FORMAT_S16_BE,
SND_PCM_FORMAT_S24_3LE,
};
snd_pcm_format_t format;
unsigned int i;
for (i = 0; i < sizeof dmix_formats / sizeof dmix_formats[0]; ++i) {
format = dmix_formats[i];
ret = snd_pcm_hw_params_set_format(spcm, hw_params, format);
if (ret >= 0)
break;
}
if (ret < 0 && dmix->type != SND_PCM_TYPE_DMIX) {
/* TODO: try to choose a good format */
ret = INTERNAL(snd_pcm_hw_params_set_format_first)(spcm, hw_params, &format);
}
if (ret < 0) {
SNDERR("requested or auto-format is not available");
return ret;
}
params->format = format;
................................
}
Google 上也沒有找到說明很清楚這樣的問題點出現在哪部份.
經過幾天的 trial and error 發現, 主要是我的 CODEC driver 並不支援dmix 所需要的 format.
dmix 只支援下列格式(由 code可以知道) .
SND_PCM_FORMAT_S32,
SND_PCM_FORMAT_S32 ^ SND_PCM_FORMAT_S32_LE ^ SND_PCM_FORMAT_S32_BE,
SND_PCM_FORMAT_S16,
SND_PCM_FORMAT_S16 ^ SND_PCM_FORMAT_S16_LE ^ SND_PCM_FORMAT_S16_BE,
SND_PCM_FORMAT_S24_3LE,
所以就先修改我的CODEC driver , 讓它支援 S16_LE (之前只有 支援 U16_LE ).
這個 bug 就解決了 , 並且 asound.conf 也可以讓 "default" 順利的轉向到 dmix 上.
所以 dmix 如果出現
ALSA lib pcm_direct.c:812:(snd_pcm_direct_initialize_slave) requested or auto-format is not available
錯誤 , 要先看看 codec driver 是否支援 dmix 需要的格式.
PS: 有關 asound.conf 可以參考下列網頁 , 我認為寫的比較清楚的.
http://forums.gentoo.tw/viewtopic.php?f=18&t=44507
2015年9月2日 星期三
在一般 AP 中模擬 kernel 的 module_init() 方式註冊功能.
最近要寫一堆測試程式, 並且將來可能會增加 , 也會隨著某些機種不同而, 有些測試項目可能會移除,增加.
我們的機器雖然是 linux , 但並非相同 (亂改了很多東西) , 最頭痛的就是 key pad input 介面.這個介面不是使用 linux 的 input 方式, 反而有點像一般 MCU 的 getkey() 方式.
所以就比較難寫 AP. 要不時的檢查有沒有 key input , 導致正個 Test AP 結構混亂.
void *frame_data;
int (*init_func)(void *arg);
int (*event_func)(void *arg,int key);
int (*timer_tick)(void *arg);
int (*exit_func)(void *arg);
簡單說明一下:
init_func => initial frame ,當這個 frame 被致能 (active) 的時候會先呼叫這個 initial .
event_func => 當有 key input 的時候會將 key value 透過這個 function 傳到 active 的 frame.
timer_tick => 每 一小段時間 (不是很準) 就會驅動這個 tick function , 可以在這個 function 中作一些 polling , timeout wait 等動作.
exit_func => 當這個 Frame 被 inactive 的時候會呼叫這個 function , 可以釋放 一些 resource .
主題來了, 每個測試 Frame 可能會增加 , 抽換 . 我想要做到每個測試 frame 都獨立, 並可以在 compile 的時候選擇要不要加入.想了想好像 kernel 的 driver init 動作 (module_init() function ). 如果我可以在 AP 中模擬 kernel 的 module_init() 方式 , 或許可以解決我的問題.
網路上google 了很多有關 kernel module_init() 的方式和 Trace 了一下 kernel source code 後, 發現也蠻簡單. 其實就是利用 compiler 的時候將 init 的 function point 放到一個 特定的 section 中, booting 過程由這 section 中取出 function point 然後執行它即可.
首先定義 frame_init() 的 macro function , 如下:
typedef void (*initlist_func)(void);
#define __initlist __attribute__((section("initlist")))
#define frame_init(fn) \
initlist_func __initlist_##fn##__ __initlist = fn
extern initlist_func __start_initlist;
extern initlist_func __stop_initlist;
要使用 __attribute__ + section, section 的名稱最好前面不要有 .xxxx , 通常 .xxxx 為預定的
section , 如 .code .text .data 等.
另外宣告兩個 start & stop point , 一般 GCC 會自動產生這兩個 point, 只要宣告拿來用即可.
使用上就是將init function 用 farme_init() 來宣告, 內容將 event-driven 的 function call 放入即可 , 如下:
void main_frame_register(void)
{
struct frame_function info;
DEBUG_MSG("");
// info.frame_index = -1; // Not need specifity.
info.frame_layer = 0;
info.frame_name = "MAIN_FRAME",
info.frame_item = NULL, // main menu is first frame , not need title.
//---- functions of main frame.
info.frame_data = (void *)&main_menu_data,
info.init_func = main_init,
info.event_func = main_event,
info.timer_tick = NULL,
info.exit_func = main_exit,
Register_Frame(&info);
}
frame_init(main_frame_register);
在程式啟動的時候進行這些 frame_init() code 的執行, code 如下:
for (init_fc_p = &__start_initlist ;
init_fc_p != &__stop_initlist ; init_fc_p ++)
(*init_fc_p)();
這樣就可以模擬 kernel 的 module_init() 的功能了,接下來就是處理如何 切換 frame , 和 派送 event-driven 對應的 function call 了.
我們的機器雖然是 linux , 但並非相同 (亂改了很多東西) , 最頭痛的就是 key pad input 介面.這個介面不是使用 linux 的 input 方式, 反而有點像一般 MCU 的 getkey() 方式.
所以就比較難寫 AP. 要不時的檢查有沒有 key input , 導致正個 Test AP 結構混亂.
所以我想了一個方式, 這個方式有點像是 event-driven 方式, 每個測試程式當作一個 Frame (因為有自己的選單, 操作介面 , 所以我暫時稱為 Frame). 每個 Frame 有固定下列幾個 even-driven 介面:
void *frame_data;
int (*init_func)(void *arg);
int (*event_func)(void *arg,int key);
int (*timer_tick)(void *arg);
int (*exit_func)(void *arg);
簡單說明一下:
init_func => initial frame ,當這個 frame 被致能 (active) 的時候會先呼叫這個 initial .
event_func => 當有 key input 的時候會將 key value 透過這個 function 傳到 active 的 frame.
timer_tick => 每 一小段時間 (不是很準) 就會驅動這個 tick function , 可以在這個 function 中作一些 polling , timeout wait 等動作.
exit_func => 當這個 Frame 被 inactive 的時候會呼叫這個 function , 可以釋放 一些 resource .
主題來了, 每個測試 Frame 可能會增加 , 抽換 . 我想要做到每個測試 frame 都獨立, 並可以在 compile 的時候選擇要不要加入.想了想好像 kernel 的 driver init 動作 (module_init() function ). 如果我可以在 AP 中模擬 kernel 的 module_init() 方式 , 或許可以解決我的問題.
網路上google 了很多有關 kernel module_init() 的方式和 Trace 了一下 kernel source code 後, 發現也蠻簡單. 其實就是利用 compiler 的時候將 init 的 function point 放到一個 特定的 section 中, booting 過程由這 section 中取出 function point 然後執行它即可.
首先定義 frame_init() 的 macro function , 如下:
typedef void (*initlist_func)(void);
#define __initlist __attribute__((section("initlist")))
#define frame_init(fn) \
initlist_func __initlist_##fn##__ __initlist = fn
extern initlist_func __start_initlist;
extern initlist_func __stop_initlist;
要使用 __attribute__ + section, section 的名稱最好前面不要有 .xxxx , 通常 .xxxx 為預定的
section , 如 .code .text .data 等.
另外宣告兩個 start & stop point , 一般 GCC 會自動產生這兩個 point, 只要宣告拿來用即可.
使用上就是將init function 用 farme_init() 來宣告, 內容將 event-driven 的 function call 放入即可 , 如下:
void main_frame_register(void)
{
struct frame_function info;
DEBUG_MSG("");
// info.frame_index = -1; // Not need specifity.
info.frame_layer = 0;
info.frame_name = "MAIN_FRAME",
info.frame_item = NULL, // main menu is first frame , not need title.
//---- functions of main frame.
info.frame_data = (void *)&main_menu_data,
info.init_func = main_init,
info.event_func = main_event,
info.timer_tick = NULL,
info.exit_func = main_exit,
Register_Frame(&info);
}
frame_init(main_frame_register);
在程式啟動的時候進行這些 frame_init() code 的執行, code 如下:
for (init_fc_p = &__start_initlist ;
init_fc_p != &__stop_initlist ; init_fc_p ++)
(*init_fc_p)();
這樣就可以模擬 kernel 的 module_init() 的功能了,接下來就是處理如何 切換 frame , 和 派送 event-driven 對應的 function call 了.
2015年8月11日 星期二
Low latency TTY UART
最近在加速 uart 的傳輸 , 發現少量 資料的時候會比較慢 , 並且慢的有點離譜.
由訊號看來 , UART 的 Rx 傳輸已經結束 ,
但是我的 read(tty)卻 大約慢10 ~ 1 mS 後才會收到.
想說是否 kernel 內部在 context switch 的時候花比較久.
於是 ''超頻" 兩倍看看是否有改善, 結果差不多一樣. !?
詭異了,竟然沒有改善, 我也使用 select () 方式看看是否加快 (之前使用 read() block type),
結果也沒有..... !?
沒法了,只好進 kernel 的 tty driver 來看看了.
TTY 的詳細結構可以參考下列 pdf file , 這個 file 應該是 linux driver (脫韁野馬) 的第 18 章節.
就不在詳細說明 TTY 了.
https://www.google.com.tw/url?sa=t&rct=j&q=&esrc=s&source=web&cd=8&cad=rja&uact=8&ved=0CFwQFjAHahUKEwiKq8vWn6DHAhVHIaYKHXIFAA4&url=https%3A%2F%2Flwn.net%2Fimages%2Fpdf%2FLDD3%2Fch18.pdf&ei=Z4HJVcrbM8fCmAXyioBw&usg=AFQjCNHg8aGyIBkWXmU3itJ1JY5xVD1usQ&sig2=gE3rZ1mU8oDQMsaoT49vEA
最後發現 UART 收到 data 後會呼叫 tty_flip_buffer_push(tty); 通知 tty layer 有 data 在 buffer 了.
tty_flip_buffer_push(tty) 內容主要有下列這段 code ( 2.6.32.19),
if (tty->low_latency)
flush_to_ldisc(&tty->buf.work.work);
else
schedule_delayed_work(&tty->buf.work, 1);
答案出來了 , 因為每次都排 schedule_delay_work 去收 buffer 的資料.
我們使用的 tick 是 100 , 所以每排一次約 10 mS, 難怪我會測量到 10 ~ 1 mS,並且和 CPU clock 無關.
好 , 那就想辦法設定 tty->low_latency 為 "1" 吧, 這樣就是直接呼叫,不使用排程.
發現 在 serial_core.c 中的 ioctl 有支援修改 serial 的參數 (TIOCSSERIAL), 設定的 flage 中有
UPF_LOW_LATENCY (新版的是使用 ASYNC_LOW_LATENCY, 都是設定相同的 bit ).
測試後發現, 被 CAP_SYS_ADMIN 擋住 , 不過設定完後先 close tty 在 open tty 卻有效.
static int uart_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd,
unsigned long arg)
{
.............
case TIOCSSERIAL:
ret = uart_set_info(state, uarg);
break;
..............
}
static int uart_set_info(struct uart_state *state,
struct serial_struct __user *newinfo)
{
.....................
if (!capable(CAP_SYS_ADMIN))
{
..........
goto exit;
.............
goto check_and_exit;
................
}
..............
if (port->tty)
port->tty->low_latency =
(uport->flags & UPF_LOW_LATENCY) ? 1 : 0;
..................
}
檢查 tty open 過程後 ,發現其時會將剛剛設定的 ASYNC_LOW_LATENCY 值給帶入,難怪設定後在 close / open 一次就會有效.
static int uart_open(struct tty_struct *tty, struct file *filp)
{
............................
tty->low_latency = (state->uart_port->flags & UPF_LOW_LATENCY) ? 1 : 0;
tty->alt_speed = 0;
................
}
因為我的 AP 並非 root 執行 , 所以也不研究 SYS_CAP_ADMIN的問題了 , AP 端就 open /close 一次就好了 , 下列是 AP 設定 ASYNC_LOW_LATENCY 的方式.
//---- turn on ASYNC_LOW_LATENCY mode.
ioctl(fd, TIOCGSERIAL, &ser_info);
ser_info.flags &= ~ASYNC_LOW_LATENCY;
#if defined(ENABLE_ASYNC_LOW_LATENCY)
ser_info.flags |= ASYNC_LOW_LATENCY;
#endif
ioctl(fd, TIOCSSERIAL, &ser_info);
DEBUG_MSG("%s,Low_latency:%s ",dev_name,
(ser_info.flags & ASYNC_LOW_LATENCY) ? "on" : "off");
close(fd);
修改後再次測量uart 訊號到 read(tty) 的時間 (這次使用 select() 方式去等 data ) ,
改善很多 , 整個 UART 傳輸速度也提升了 !
仔細想想 , 原本 UART Rx IRQ 用 schedule_delay_work() 排程去執行, 符合 Top and Bottom Halves 方式 , 打開 ASYNC_LOW_LATENCY 後卻是 IRQ 就直接做完, 導致有可能 UART 的 Rx IRQ 執行時間攏長.
所以要打開 ASYNC_LOW_LATENCY 要考慮一下整個系統的狀況 !!
由訊號看來 , UART 的 Rx 傳輸已經結束 ,
但是我的 read(tty)卻 大約慢10 ~ 1 mS 後才會收到.
想說是否 kernel 內部在 context switch 的時候花比較久.
於是 ''超頻" 兩倍看看是否有改善, 結果差不多一樣. !?
詭異了,竟然沒有改善, 我也使用 select () 方式看看是否加快 (之前使用 read() block type),
結果也沒有..... !?
沒法了,只好進 kernel 的 tty driver 來看看了.
TTY 的詳細結構可以參考下列 pdf file , 這個 file 應該是 linux driver (脫韁野馬) 的第 18 章節.
就不在詳細說明 TTY 了.
https://www.google.com.tw/url?sa=t&rct=j&q=&esrc=s&source=web&cd=8&cad=rja&uact=8&ved=0CFwQFjAHahUKEwiKq8vWn6DHAhVHIaYKHXIFAA4&url=https%3A%2F%2Flwn.net%2Fimages%2Fpdf%2FLDD3%2Fch18.pdf&ei=Z4HJVcrbM8fCmAXyioBw&usg=AFQjCNHg8aGyIBkWXmU3itJ1JY5xVD1usQ&sig2=gE3rZ1mU8oDQMsaoT49vEA
最後發現 UART 收到 data 後會呼叫 tty_flip_buffer_push(tty); 通知 tty layer 有 data 在 buffer 了.
tty_flip_buffer_push(tty) 內容主要有下列這段 code ( 2.6.32.19),
if (tty->low_latency)
flush_to_ldisc(&tty->buf.work.work);
else
schedule_delayed_work(&tty->buf.work, 1);
答案出來了 , 因為每次都排 schedule_delay_work 去收 buffer 的資料.
我們使用的 tick 是 100 , 所以每排一次約 10 mS, 難怪我會測量到 10 ~ 1 mS,並且和 CPU clock 無關.
好 , 那就想辦法設定 tty->low_latency 為 "1" 吧, 這樣就是直接呼叫,不使用排程.
發現 在 serial_core.c 中的 ioctl 有支援修改 serial 的參數 (TIOCSSERIAL), 設定的 flage 中有
UPF_LOW_LATENCY (新版的是使用 ASYNC_LOW_LATENCY, 都是設定相同的 bit ).
測試後發現, 被 CAP_SYS_ADMIN 擋住 , 不過設定完後先 close tty 在 open tty 卻有效.
static int uart_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd,
unsigned long arg)
{
.............
case TIOCSSERIAL:
ret = uart_set_info(state, uarg);
break;
..............
}
static int uart_set_info(struct uart_state *state,
struct serial_struct __user *newinfo)
{
.....................
if (!capable(CAP_SYS_ADMIN))
{
..........
goto exit;
.............
goto check_and_exit;
................
}
..............
if (port->tty)
port->tty->low_latency =
(uport->flags & UPF_LOW_LATENCY) ? 1 : 0;
..................
}
檢查 tty open 過程後 ,發現其時會將剛剛設定的 ASYNC_LOW_LATENCY 值給帶入,難怪設定後在 close / open 一次就會有效.
static int uart_open(struct tty_struct *tty, struct file *filp)
{
............................
tty->low_latency = (state->uart_port->flags & UPF_LOW_LATENCY) ? 1 : 0;
tty->alt_speed = 0;
................
}
因為我的 AP 並非 root 執行 , 所以也不研究 SYS_CAP_ADMIN的問題了 , AP 端就 open /close 一次就好了 , 下列是 AP 設定 ASYNC_LOW_LATENCY 的方式.
//---- turn on ASYNC_LOW_LATENCY mode.
ioctl(fd, TIOCGSERIAL, &ser_info);
ser_info.flags &= ~ASYNC_LOW_LATENCY;
#if defined(ENABLE_ASYNC_LOW_LATENCY)
ser_info.flags |= ASYNC_LOW_LATENCY;
#endif
ioctl(fd, TIOCSSERIAL, &ser_info);
DEBUG_MSG("%s,Low_latency:%s ",dev_name,
(ser_info.flags & ASYNC_LOW_LATENCY) ? "on" : "off");
close(fd);
修改後再次測量uart 訊號到 read(tty) 的時間 (這次使用 select() 方式去等 data ) ,
改善很多 , 整個 UART 傳輸速度也提升了 !
仔細想想 , 原本 UART Rx IRQ 用 schedule_delay_work() 排程去執行, 符合 Top and Bottom Halves 方式 , 打開 ASYNC_LOW_LATENCY 後卻是 IRQ 就直接做完, 導致有可能 UART 的 Rx IRQ 執行時間攏長.
所以要打開 ASYNC_LOW_LATENCY 要考慮一下整個系統的狀況 !!
2015年1月5日 星期一
ioctl command EVIOCGRAB of linux input layer.
最近這一週都在處理有關 Linux input 問題,詳細就不說了,主要這次對 /dev/input/eventX 做了稍微詳細的測試一下.
有關取 key 的方式就是open & read ,這也是一般方式,問題是open 兩次得到兩個handle id 分別對這兩個去讀取會得到啥呢?一個按鍵的值會給哪一個呢? 結果是兩者都會收到 , 並且先 open 先收到 , 這樣說可能不是很清楚 , 舉個例子說明吧.
先利用 open 開啟 input device , 並且不要關閉(close) ,然後輸入 A,B,C,D & E , 接著再open input device 一次 , 然後 輸入 F,G,H,U,J & K , 接著讀取兩個 handle file ID . 第一個 open 的會讀取到 A,B,C,D,E,F,G,H,I,J & K , 第二次 open 的會讀取到 F,G,H,I,J & K . 這樣應該了解我說的先 open 先讀到的意思了吧 ! 想一想, 這樣的邏輯也對 , open 一次就是新的一個 file , 之前已經過去的 key ( open 之前) 當然收不到 .
問題又來了 , 這樣會導致兩個 task 都收到 key , 這樣的情況下 , 我能只限定某個 task 收到 , 另外一個不要收到嗎 ?? 這就是今天的主題了.
可以利用今天說的主題 EVIOCGRAB ioctl command 來進行這樣的控制 , 對於想要 "獨佔" 的 device file 下 EVIOCGRAB command , 就可以確保只有它收到 , 其它有 open 的 device 不會收到. 如果兩者都下 EVIOCGRAB command , 就看誰先了, 先的就 "獨占" .
下面就是我測試的 sample code (部份), 會發現只有第二次 open 的會讀到輸入值.
devfd_1 = open(key_dev, O_RDONLY | O_NONBLOCK);
devfd = open(key_dev, O_RDONLY | O_NONBLOCK);
ioctl(devfd, EVIOCGRAB,1);
ioctl(devfd_1, EVIOCGRAB,1);
ioctl(devfd_1, EVIOCGRAB,1);
for ( i = 0 ; i < 20 ; i ++ )
{
readlen = read(devfd,&inputdata,sizeof(struct input_event));
if ( readlen == sizeof(struct input_event) && (inputdata.type == EV_KEY) )
{
printf("Keypad code:%d",inputdata.code);
{
readlen = read(devfd,&inputdata,sizeof(struct input_event));
if ( readlen == sizeof(struct input_event) && (inputdata.type == EV_KEY) )
{
printf("Keypad code:%d",inputdata.code);
if (inputdata.value == 1)
printf(" Down!\n");
else if (inputdata.value == 0)
printf(" Up!\n");
else if (inputdata.value == 2 )
printf(" PRESSED !\n");
}
readlen = read(devfd_1,&inputdata,sizeof(struct input_event));
if ( readlen == sizeof(struct input_event) && (inputdata.type == EV_KEY) )
{
printf("2 Keypad code:%d",inputdata.code);
if (inputdata.value == 1)
printf(" Down!\n");
else if (inputdata.value == 0)
printf(" Up!\n");
else if (inputdata.value == 2 )
printf(" PRESSED !\n");
}
}
close(devfd);
close(devfd_1);
2014年6月10日 星期二
The pthread_cond_signal() VS pthread_cond_broadcast()
上一篇說到了 pthread_cond_wati() , 現在說說 pthread_cond_signal() & pthread_cond_brocast() 吧 .
基本上這兩個 都是傳 condiction 給 ptherad , 讓 pthread 由 wait 狀態 wakeup 起來 . 不過有些差別 pthread_cond_signal() 只會讓一個 pthread wakeup (等待同一個 condition 的 pthread) ,至於是哪一個 pthread wakeup 呢 ? 目前我沒有去詳細 研究 , 不過這個signal 不能確定(指定)那一個 pthread , 所以使用上要小心點. 而 boradcast 是讓所有 pthread 都 wakeup 起來 .
所以依照所需選擇合適的function. 我通常都只有一個 ptherad 在等待 , 所以我都用 brocast , 確保我的 pthread 可以 wakeup .
下面有參考範例 ,
Reference :
http://publib.boulder.ibm.com/iseries/v5r1/ic2987/index.htm?info/apis/users_76.htm
http://publib.boulder.ibm.com/iseries/v5r1/ic2987/index.htm?info/apis/users_73.htm
PS. 目前比較忙沒有時間按照範例測試.
基本上這兩個 都是傳 condiction 給 ptherad , 讓 pthread 由 wait 狀態 wakeup 起來 . 不過有些差別 pthread_cond_signal() 只會讓一個 pthread wakeup (等待同一個 condition 的 pthread) ,至於是哪一個 pthread wakeup 呢 ? 目前我沒有去詳細 研究 , 不過這個signal 不能確定(指定)那一個 pthread , 所以使用上要小心點. 而 boradcast 是讓所有 pthread 都 wakeup 起來 .
所以依照所需選擇合適的function. 我通常都只有一個 ptherad 在等待 , 所以我都用 brocast , 確保我的 pthread 可以 wakeup .
下面有參考範例 ,
Reference :
http://publib.boulder.ibm.com/iseries/v5r1/ic2987/index.htm?info/apis/users_76.htm
http://publib.boulder.ibm.com/iseries/v5r1/ic2987/index.htm?info/apis/users_73.htm
PS. 目前比較忙沒有時間按照範例測試.
2014年6月4日 星期三
The pthread_cond_wait() and pthread_cond_timedwait()
最近在弄 power daemon ,需要使用大量 thread , 並且不能讓這些 thread " free running " , 所以再次研究一下 pthread_cond_wait() & pthread_cond_timedwait() !
先來看看 pthread_cond_wait() 的說明吧 ! (下列 藍色文字擷取於 man 說明)
pthread_cond_wait() 的動作會先取得 cond 的內容 , 然後 unlock 讓 ptherad_cond_signal() 可以更改 cond 的內容,接著再 lock , 並判斷是否需要繼續 ptherad , 如果沒有就 unlock 並停下 pthread, 相反就 lock 且接著 pthread 的內容.
pthread_cond_wait(mutex, cond):
value = cond->value; /* 1 */
pthread_mutex_unlock(mutex); /* 2 */
pthread_mutex_lock(cond->mutex); /* 10 */
if (value == cond->value) { /* 11 */
me->next_cond = cond->waiter;
cond->waiter = me;
pthread_mutex_unlock(cond->mutex);
unable_to_run(me);
} else
pthread_mutex_unlock(cond->mutex); /* 12 */
pthread_mutex_lock(mutex); /* 13 */
value = cond->value; /* 1 */
pthread_mutex_unlock(mutex); /* 2 */
pthread_mutex_lock(cond->mutex); /* 10 */
if (value == cond->value) { /* 11 */
me->next_cond = cond->waiter;
cond->waiter = me;
pthread_mutex_unlock(cond->mutex);
unable_to_run(me);
} else
pthread_mutex_unlock(cond->mutex); /* 12 */
pthread_mutex_lock(mutex); /* 13 */
我們再看看 pthread_cond_signal() 的動作吧.
首先會先 lock , 如果 lock 失敗會繼續等在 lock 階段, lock 成功會更動 cond 內容,並且叫醒 pthread. 然後 unlock 讓 pthread 可以繼續執行.
pthread_cond_signal(cond):
pthread_mutex_lock(cond->mutex); /* 3 */
cond->value++; /* 4 */
if (cond->waiter) { /* 5 */
sleeper = cond->waiter; /* 6 */
cond->waiter = sleeper->next_cond; /* 7 */
able_to_run(sleeper); /* 8 */
}
pthread_mutex_unlock(cond->mutex); /* 9 */
說這麼多 ,來個範例吧 :
下列兩個範例 , 一個是 pthread 本身 , 做完事情後等著 pthread_cond_signal() 來進行離開.
所以可以看到 pthread 執行時先 lock , 最後停在 pthread_cond_wait() . 等另外 function
送出 pthread_cond_signal() , pthread 就會繼續執行,並且離開 pthread.
{
pthread_mutex_lock(&thread_mutex);
/* Loop forever */
for ( ps_thread_status = 1; ps_thread_status; )
{
//---- TO DO.
pthread_cond_wait(&thread_cond, &thread_mutex);
} /* End of for() */
pthread_mutex_unlock(&thread_mutex);
pthread_exit ( 0 );
}
void ps_thread_destory(void)
{
ps_thread_status = 0;
pthread_cond_signal(&thread_cond); // force weakup thread , then stop itself.
pthread_join(thread_ps_id, NULL); // wait for thread end.
pthread_mutex_destroy(&thread_mutex);
pthread_cond_destroy(&thread_cond);
}
{
ps_thread_status = 0;
pthread_cond_signal(&thread_cond); // force weakup thread , then stop itself.
pthread_join(thread_ps_id, NULL); // wait for thread end.
pthread_mutex_destroy(&thread_mutex);
pthread_cond_destroy(&thread_cond);
}
如果想要讓 pthread 暫停一段時間 後繼續執行 , 那就需要 pthread_cond_timedwait() 了.
範例如下 , 每一秒執行 for loop 一次.(紅色部分 是修改)
利用 pthread_cond_timedwait() return 的 value , 就可以判別是 timeout 還是被 signal 叫醒.
{
pthread_mutex_lock(&thread_mutex);
/* Loop forever */
for ( ps_thread_status = 1; ps_thread_status; )
{
//---- TO DO.
gettimeofday(&tv, &tz);
ts.tv_sec = tv.tv_sec + 1 ;
ts.tv_nsec = tv.tv_usec*1000;
retval = pthread_cond_timedwait(&thread_cond, &thread_mutex, &ts);
if ( retval == ETIMEDOUT)
{
//==== time out.
//---- TO DO.
}
else
{
//==== Got signal.
//---- TO DO.
}
} /* End of for() */
pthread_mutex_unlock(&thread_mutex);
pthread_exit ( 0 );
}
這樣的架構下就可以控制 pthread 進行一些流程控制了.
2014年6月3日 星期二
EPERM error of settimeofday() , using capset() & capget() modify CAP_SYS_TIME.
最近發現 settimeofday() 沒有辦法執行 , 回報錯誤為 -1 (EPERM), 看一下 man 說明,發現沒有打開 CAP_SYS_TIME, 所以在 euid >= 1000 的時候不會允許設定系統時間.
下列是 man settimeofday() 的說明:
EPERM The calling process has insufficient privilege to call settimeofday();
under Linux the CAP_SYS_TIME capability is required.
好吧 , 打開一下後門 , google 了一下,發現可以使用 capget() & capset來設定.
大致說明一下 CAP 的設定, 首先要先了解三個CAP 的等級.
A. effective => 有效的, 這個有設定就可以允許 操作相對應的東西 ,
例如 CAP_SYS_TIME 有設定就可以設定系統時間.
B. permitted => 這是允許的內容 , 如果這個資料內的 CAP_SYS_TIME 沒有設定 ,
那設定 effective 也沒用.
C. inheritable => 是否可被繼承 ,如果有設定 , fork() + exec() 的 子 process 也可以繼續
被允許對應的設定.
typedef struct __user_cap_data_struct {
__u32 effective;
__u32 permitted;
__u32 inheritable;
} *cap_user_data_t;
來個實際例子吧, 目前子程序的 P (permitted) 是全部被允許的 ,
不過E(effective)沒有打開 ,利用下列的範例將其打開 , 就可以使用 settimeofday() 設定系統時間了.
if (geteuid() > 0 )
{
//---- Not Root ,need turn on CAP_SYS_TIME.
header.version = _LINUX_CAPABILITY_VERSION_1;
header.pid = getpid();
retval = capget(&header, &cap);
if(retval)
goto capget_fail;
// printf("\t[%s] PID:%d E:%x P:%x I:%x\n", __FUNCTION__,
// header.pid, cap.effective, cap.permitted,cap.inheritable);
header.version = _LINUX_CAPABILITY_VERSION_1;
header.pid = 0;
if ( enable )
cap.effective |= (1 << CAP_SYS_TIME);
else
cap.effective &= ~(1 << CAP_SYS_TIME);
retval = capset(&header, &cap);
if(retval)
goto capset_fail;
}
PS. 這段 function call 我想要打開 CAP_SYS_TIME 設定系統時間後就關閉(我提供一個特別的 SO API , 所以使用者還是不能 設定 系統時間,除非透過這個 API .
所以先判別是否為 root (利用 EUID) , 如果不是就可以enable / disable . 如果是 root , 就不理會(root 本來就可以)
下列是 man settimeofday() 的說明:
EPERM The calling process has insufficient privilege to call settimeofday();
under Linux the CAP_SYS_TIME capability is required.
好吧 , 打開一下後門 , google 了一下,發現可以使用 capget() & capset來設定.
大致說明一下 CAP 的設定, 首先要先了解三個CAP 的等級.
A. effective => 有效的, 這個有設定就可以允許 操作相對應的東西 ,
例如 CAP_SYS_TIME 有設定就可以設定系統時間.
B. permitted => 這是允許的內容 , 如果這個資料內的 CAP_SYS_TIME 沒有設定 ,
那設定 effective 也沒用.
C. inheritable => 是否可被繼承 ,如果有設定 , fork() + exec() 的 子 process 也可以繼續
被允許對應的設定.
typedef struct __user_cap_data_struct {
__u32 effective;
__u32 permitted;
__u32 inheritable;
} *cap_user_data_t;
來個實際例子吧, 目前子程序的 P (permitted) 是全部被允許的 ,
不過E(effective)沒有打開 ,利用下列的範例將其打開 , 就可以使用 settimeofday() 設定系統時間了.
if (geteuid() > 0 )
{
//---- Not Root ,need turn on CAP_SYS_TIME.
header.version = _LINUX_CAPABILITY_VERSION_1;
header.pid = getpid();
retval = capget(&header, &cap);
if(retval)
goto capget_fail;
// printf("\t[%s] PID:%d E:%x P:%x I:%x\n", __FUNCTION__,
// header.pid, cap.effective, cap.permitted,cap.inheritable);
header.version = _LINUX_CAPABILITY_VERSION_1;
header.pid = 0;
if ( enable )
cap.effective |= (1 << CAP_SYS_TIME);
else
cap.effective &= ~(1 << CAP_SYS_TIME);
retval = capset(&header, &cap);
if(retval)
goto capset_fail;
}
PS. 這段 function call 我想要打開 CAP_SYS_TIME 設定系統時間後就關閉(我提供一個特別的 SO API , 所以使用者還是不能 設定 系統時間,除非透過這個 API .
所以先判別是否為 root (利用 EUID) , 如果不是就可以enable / disable . 如果是 root , 就不理會(root 本來就可以)
2014年5月26日 星期一
Linux Message queue IPC.
最近因為工作關係 , 需要使用一個 IPC (一對多 ) 的方式來控制 , 並且 server 需要有 blocking 的動作 (等待 server 發訊息在開始動作 ) , 本人又不想使用 sock (傳說中比較耗支援 ) .
比對並且找了一下後發現 Message queue 比較適合 .
msgrcv(); 如果不要加上IPC_NOWAIT , 就可以達到 blocking 的動作, 這樣就不會佔用太多CPU Resource .
msgrcv(); 也有一個參數 msgtype , 這個參數會對 queue 上的 masg 進行篩選 (type 相同才會接收) , 這個也可以達成一對多的訊息傳遞.
PS. msgtype = 0 , 所有訊息都收 , msgtype = xx , 只接收 type 為 xx 的message, msgtype = - yy , 接收 type 低於 yy 的訊息 (msgtype 是一個 long 的變數,所以都是數值, 這個數值不能是負數 ) .
使用上其實很簡單 , 大致份為 四個操作 function .
A. msgget()
建立或取得 message queue.
B. msgsend()
傳訊息資料到 message queue.
C. msgrcv()
從 message queue 取得訊息資料.
D. msgctl() with IPC_RMID.
最後不用 message queue 的時候, 將 這個 message queue 刪除 (distore).
不多說, 來幾個 example code 吧 !
A.msgget()
給個特別(識別)的 key .
msgflg 帶入 權限或是 IPC_CREAT (如果不存在就建立).
最傳回 handle id .
msgflg = IPC_CREAT | 0666;
m_msq_id = msgget((key_t)MSQ_CMD_KEYID, msgflg);
B. msgsnd()
我的使用上是對多個 server 進行播送 , 所以使用的 IPC_NOWAIT.
其中 data內的 mtype 需要設定這個 message 的type,如上面所敘述就可以讓
msgrcv 依照type 接收.
retval = msgsnd(m_msq_id, data, sizeof(struct msgbuf), IPC_NOWAIT);
C. msgrcv()
這邊需要注意type 參數,就如同上面敘述,可以 0 , xx or -yy .
最後一個 參數是 flag , 可以有 IPC_NOWAIT 等.
我需要 blocking , 所以帶 "0" .
rcv_siz = msgrcv(m_msq_id,data,sizeof(struct msgbuf),type,0);
D. msgctl()
這個有很多控制參數可以用, 不過我只使用最後 distore 的功能.
範例如下:
retval = msgctl(m_msq_id, IPC_RMID, (struct msqid_ds *)NULL);
Reference:
下列這個網頁有詳細的說明和一些 example code .
http://www.cs.cf.ac.uk/Dave/C/node25.html
比對並且找了一下後發現 Message queue 比較適合 .
msgrcv(); 如果不要加上IPC_NOWAIT , 就可以達到 blocking 的動作, 這樣就不會佔用太多CPU Resource .
msgrcv(); 也有一個參數 msgtype , 這個參數會對 queue 上的 masg 進行篩選 (type 相同才會接收) , 這個也可以達成一對多的訊息傳遞.
PS. msgtype = 0 , 所有訊息都收 , msgtype = xx , 只接收 type 為 xx 的message, msgtype = - yy , 接收 type 低於 yy 的訊息 (msgtype 是一個 long 的變數,所以都是數值, 這個數值不能是負數 ) .
使用上其實很簡單 , 大致份為 四個操作 function .
A. msgget()
建立或取得 message queue.
B. msgsend()
傳訊息資料到 message queue.
C. msgrcv()
從 message queue 取得訊息資料.
D. msgctl() with IPC_RMID.
最後不用 message queue 的時候, 將 這個 message queue 刪除 (distore).
不多說, 來幾個 example code 吧 !
A.msgget()
給個特別(識別)的 key .
msgflg 帶入 權限或是 IPC_CREAT (如果不存在就建立).
最傳回 handle id .
msgflg = IPC_CREAT | 0666;
m_msq_id = msgget((key_t)MSQ_CMD_KEYID, msgflg);
B. msgsnd()
我的使用上是對多個 server 進行播送 , 所以使用的 IPC_NOWAIT.
其中 data內的 mtype 需要設定這個 message 的type,如上面所敘述就可以讓
msgrcv 依照type 接收.
retval = msgsnd(m_msq_id, data, sizeof(struct msgbuf), IPC_NOWAIT);
C. msgrcv()
這邊需要注意type 參數,就如同上面敘述,可以 0 , xx or -yy .
最後一個 參數是 flag , 可以有 IPC_NOWAIT 等.
我需要 blocking , 所以帶 "0" .
rcv_siz = msgrcv(m_msq_id,data,sizeof(struct msgbuf),type,0);
D. msgctl()
這個有很多控制參數可以用, 不過我只使用最後 distore 的功能.
範例如下:
retval = msgctl(m_msq_id, IPC_RMID, (struct msqid_ds *)NULL);
Reference:
下列這個網頁有詳細的說明和一些 example code .
http://www.cs.cf.ac.uk/Dave/C/node25.html
2014年5月21日 星期三
Booting time.
最近在最佳化系統工作 , 主要 force on booting time .
後來發現 , 第一個程式 loading 完到執行 main() 卻要花很久 .
實驗範例如下 :
int main(int argc, char **argv)
{
.......
printf ("xxxxx \n );
.........
.........
}
發現 printf 大約要 2 秒後才會列印出來 , Why ?? 花很多時間在 loading library 嗎 ?
(當然我的 cpu 只有 133MHz , 所以才會花這樣久 , 如何測量呢 ? 可以利用 bootchart 搭配來測量 , 雖說不是很準, 不過可以看到有所 delay time )
結果發現 ,只要在 .so file 的 code 內加入下列這樣的宣告 Function .
就會在 loading .so 的時候去執行 ,如果這樣的 function 花費很多時間處理 , 當然 running main() 的時間也會延後 .
__attribute__ ((constructor)) void test_font_init(void)
{
..........
}
PS. 主要是宣告 constructor這個屬性 , 有關這個 屬性的詳細內容 , 使用方式 ,可以去 google 找一下,一堆說明 .
後來發現 , 第一個程式 loading 完到執行 main() 卻要花很久 .
實驗範例如下 :
int main(int argc, char **argv)
{
.......
printf ("xxxxx \n );
.........
.........
}
發現 printf 大約要 2 秒後才會列印出來 , Why ?? 花很多時間在 loading library 嗎 ?
(當然我的 cpu 只有 133MHz , 所以才會花這樣久 , 如何測量呢 ? 可以利用 bootchart 搭配來測量 , 雖說不是很準, 不過可以看到有所 delay time )
結果發現 ,只要在 .so file 的 code 內加入下列這樣的宣告 Function .
就會在 loading .so 的時候去執行 ,如果這樣的 function 花費很多時間處理 , 當然 running main() 的時間也會延後 .
__attribute__ ((constructor)) void test_font_init(void)
{
..........
}
PS. 主要是宣告 constructor這個屬性 , 有關這個 屬性的詳細內容 , 使用方式 ,可以去 google 找一下,一堆說明 .
2014年4月21日 星期一
bootchart in busybox .
最近在檢視開機時間 , 記憶中有一個 bootchart 可用.
先來看看目前 bootchart 發展到如何 , 有哪些 tools 可使用.下列網站說明的很清楚,也有很多資源可以使用.
http://elinux.org/Bootchart
A. 觀看一下目前工作的機器.發現使用的 busybox 已經有內建 bootchart 了.
事情好辦多了 , 接著開始產生 bootchart 資料吧.
B.基本上應該將kernel command line 的 init=./init 改為 init=/sbin/bootchart .
不過我只想觀看一下rcS 啟動後有哪些 task 號時間.
所以就在 rcS 的第一行加上 "/sbin/bootchart init & " 這樣就可以了.
C.產生的資料為 bootchart.gz , 並且放在 /var/log 中.
解開 bootchart.gz 會發現一堆文字檔案,很難關看booting 過程中的訊息.
D.下列網址(bootchart2) 提供了一個可以將這些文字檔案轉成 png file 的 utility.
來安裝吧 ..... !
https://github.com/mmeeks/bootchart
1. 下載 source code.
2. 依照說明文件, 只要進行 " make " 即可 (Fedora 20 i686 ).
3. 將 target 上的 /var/log/bootchart.gz 上傳回 Fedora (我慣用 fedora )
4. [jeff@...]$ /home/<your bootchart dir>/bootchart-master/pybootchartgui.py bootchart.tgz
5. 就會產生 bootchart.png file 了.
E. bootchart 圖片就不在上傳了, 網路上都有很多 .
先來看看目前 bootchart 發展到如何 , 有哪些 tools 可使用.下列網站說明的很清楚,也有很多資源可以使用.
http://elinux.org/Bootchart
A. 觀看一下目前工作的機器.發現使用的 busybox 已經有內建 bootchart 了.
事情好辦多了 , 接著開始產生 bootchart 資料吧.
B.基本上應該將kernel command line 的 init=./init 改為 init=/sbin/bootchart .
不過我只想觀看一下rcS 啟動後有哪些 task 號時間.
所以就在 rcS 的第一行加上 "/sbin/bootchart init & " 這樣就可以了.
C.產生的資料為 bootchart.gz , 並且放在 /var/log 中.
解開 bootchart.gz 會發現一堆文字檔案,很難關看booting 過程中的訊息.
D.下列網址(bootchart2) 提供了一個可以將這些文字檔案轉成 png file 的 utility.
來安裝吧 ..... !
https://github.com/mmeeks/bootchart
1. 下載 source code.
2. 依照說明文件, 只要進行 " make " 即可 (Fedora 20 i686 ).
3. 將 target 上的 /var/log/bootchart.gz 上傳回 Fedora (我慣用 fedora )
4. [jeff@...]$ /home/<your bootchart dir>/bootchart-master/pybootchartgui.py bootchart.tgz
5. 就會產生 bootchart.png file 了.
E. bootchart 圖片就不在上傳了, 網路上都有很多 .
2014年4月11日 星期五
Printf 不能輸出到 console ....
最近工作上發現 , 有使用者的程式使用 printf 來 debug , 不過卻沒有輸出 .
網路上有說使用 fprintf + stdout or stderr 等方式 . 不過在我們的環境下卻沒有辦法 .
最後 , 我使用 暴力方式 解決了 !!
首先 , 先了解 console 是哪個 tty . 然後在 程式內 直接 fopen tty , 接著用 fprointf 的方式直接寫過去就可以了 !!
雖然這樣的方式不好 , 不過先可以用 , 有時間在研究一下正規方式 .
============================
#define AP_DEBUG_ON
#if defined (AP_DEBUG_ON)
#define DEBUG_MSG(S,A...) fprintf(ttyfd,"==== [%s]:[%d]:" S "\n",__FUNCTION__,__LINE__,## A)
#else
#define DEBUG_MSG(S,A...) do {} while (0)
#endif
FILE *ttyfd ;
int main(int argc, char **argv)
{
ttyfd = fopen("/dev/ttyS0", "w");
...................................
DEBUG_MSG("flag:%d -> %d ",flag,flag ? 0 : 1 );
}
網路上有說使用 fprintf + stdout or stderr 等方式 . 不過在我們的環境下卻沒有辦法 .
最後 , 我使用 暴力方式 解決了 !!
首先 , 先了解 console 是哪個 tty . 然後在 程式內 直接 fopen tty , 接著用 fprointf 的方式直接寫過去就可以了 !!
雖然這樣的方式不好 , 不過先可以用 , 有時間在研究一下正規方式 .
============================
#define AP_DEBUG_ON
#if defined (AP_DEBUG_ON)
#define DEBUG_MSG(S,A...) fprintf(ttyfd,"==== [%s]:[%d]:" S "\n",__FUNCTION__,__LINE__,## A)
#else
#define DEBUG_MSG(S,A...) do {} while (0)
#endif
FILE *ttyfd ;
int main(int argc, char **argv)
{
ttyfd = fopen("/dev/ttyS0", "w");
...................................
DEBUG_MSG("flag:%d -> %d ",flag,flag ? 0 : 1 );
}
2014年3月25日 星期二
Using "systemctl" enable smb.service in Fedora 20
最近在使用 Fedora 20 發現 每次開機 samba 都不通 , 需要執行一次 system-config-samba 才能動作.
google 一下 , 發現主要是 smb.service 沒有啟動 , 我知道舊版的使用 setup 指令來設定啟動.
Fedora 20 就不是使用這樣的方式 , 而是使用 systemctl 來啟動.
所以安裝好 system-config-samba 並設定好 samba 後需要利用 systemctl 來啟動. 指令如下:
[jeff@localhost ~]$ su -c 'systemctl enable smb.service '
google 一下 , 發現主要是 smb.service 沒有啟動 , 我知道舊版的使用 setup 指令來設定啟動.
Fedora 20 就不是使用這樣的方式 , 而是使用 systemctl 來啟動.
所以安裝好 system-config-samba 並設定好 samba 後需要利用 systemctl 來啟動. 指令如下:
[jeff@localhost ~]$ su -c 'systemctl enable smb.service '
2013年12月5日 星期四
modify geanyvc for support subversion 1.8.4
更換 SVN 1.8.4 後發現我常用的 geany + geanyvc &meld 卻不能使用 了 .
主要原因是因為 geanyvc 沒有辦法找到 .svn 資料夾 .
看一下 google 上的討論 , 只要修改兩個小地方就可以用了, 那就動手升級 geany 和 geanyvc 吧 !!
下載 source code .
geany : "geany-1.23.1.tar.gz"
geany plugins : "geany-plugins-1.23.tar.gz"
先編譯 geany:
A. untar source code .
B. into source tree , do ./configure
這時候會遇到問題, 缺少 gtk ... glib 等....
$> sudo apt-get install gtk+-2.0
如果缺少 intltool-update , 就在安裝一次 .
$> sudo apt-get install intltool
C. make
D. sudo make install
這樣就有 geany 1.23.1 版了.不過記住移除就的 geany , 避免有所衝突.
接著編譯 geany-plugins :
A. untar source code.
B. 修改 ..../geany-plugins-1.23/geanyvc/src/vc_svn.c file.
static gchar * get_base_dir(const gchar * path)
{
.................................................
break;
}
while (strcmp(base, base_prev) != 0);
//---- Add patch by Jeff Hsieh.
if (base_prev == NULL)
{
/* fallback for Subversion 1.7: try to climb up the tree until we
* find a .svn subdirectory */
base_prev = find_subdir_path(path, ".svn");
}
g_free(base);
return base_prev
}
還有將下列兩行移除.
static gboolean in_vc_svn(const gchar * filename)
{
.............................
gboolean ret = FALSE;
gchar *std_output;
//---- Remove by Jeff Hsieh.
// if (!find_dir(filename, ".svn", FALSE))
// return FALSE;
if (g_file_test(filename, G_FILE_TEST_IS_DIR))
return TRUE;
.................................
}
這些修改在 google 上都找的到 , 詳細討論請自行到 google 上搜尋.
C. into source tree of top .
$> ./configure --enable-geanyvc
預設應該會打咖 geanvc , 和其他一堆 plugins .... 不過我確保有我要的 geanyvc , 所以特別加註 .
D. make
E. sudo make install
完成了.... 這樣的 geany + geanyvc 就可以支援 meld 的 diff viewer 了 .......
主要原因是因為 geanyvc 沒有辦法找到 .svn 資料夾 .
看一下 google 上的討論 , 只要修改兩個小地方就可以用了, 那就動手升級 geany 和 geanyvc 吧 !!
下載 source code .
geany : "geany-1.23.1.tar.gz"
geany plugins : "geany-plugins-1.23.tar.gz"
先編譯 geany:
A. untar source code .
B. into source tree , do ./configure
這時候會遇到問題, 缺少 gtk ... glib 等....
$> sudo apt-get install gtk+-2.0
如果缺少 intltool-update , 就在安裝一次 .
$> sudo apt-get install intltool
C. make
D. sudo make install
這樣就有 geany 1.23.1 版了.不過記住移除就的 geany , 避免有所衝突.
接著編譯 geany-plugins :
A. untar source code.
B. 修改 ..../geany-plugins-1.23/geanyvc/src/vc_svn.c file.
static gchar * get_base_dir(const gchar * path)
{
.................................................
break;
}
while (strcmp(base, base_prev) != 0);
//---- Add patch by Jeff Hsieh.
if (base_prev == NULL)
{
/* fallback for Subversion 1.7: try to climb up the tree until we
* find a .svn subdirectory */
base_prev = find_subdir_path(path, ".svn");
}
g_free(base);
return base_prev
}
還有將下列兩行移除.
static gboolean in_vc_svn(const gchar * filename)
{
.............................
gboolean ret = FALSE;
gchar *std_output;
//---- Remove by Jeff Hsieh.
// if (!find_dir(filename, ".svn", FALSE))
// return FALSE;
if (g_file_test(filename, G_FILE_TEST_IS_DIR))
return TRUE;
.................................
}
這些修改在 google 上都找的到 , 詳細討論請自行到 google 上搜尋.
C. into source tree of top .
$> ./configure --enable-geanyvc
預設應該會打咖 geanvc , 和其他一堆 plugins .... 不過我確保有我要的 geanyvc , 所以特別加註 .
D. make
E. sudo make install
完成了.... 這樣的 geany + geanyvc 就可以支援 meld 的 diff viewer 了 .......
2013年11月30日 星期六
install Subversion Ver:1.8.4 in ubuntu 12.04LTS
舊版本的SVN 會在每個目錄面都建立一個 .SVN 資料夾.
新版的 SVN (好像 1.7 以上) 就不會有這樣的狀況了 ,
只有根目錄建立一個.svn folder , 這樣有些好處, 我常常找一個接近的應用碼, 然後將整個目錄複製成另外一個名稱,然後開始新的程式, 最後要 commit 的時候就出現大問題的,因為我也複製的一個 .svn 資料夾,導致所有 svn 動作怪怪的 !! 新版就不會有這樣的問題了..
另外,可能不需要到處尋找.svn 資料夾, 所以 svn st 感覺好像有快一點....
不過要再 ubuntu 12.04LTS 上裝 SVN version 1.8.4就會出現一堆問題了 @@ .
我的習慣都是利用 source code 在環境上 make ; make install 的方式.
好吧...自己來裝...
A. Download subversion V1.8.4 source code form subversion official side.
"subversion-1.8.4.tar.gz"
B. The svn 1.8.4 need sqlite3 , so download sqlite3 source from offical side.
"sqlite-autoconf-3080100.tar.gz"
C. If the SVN need suppoer http or https , need libserf & openSSH .
# The openSSH your can install with
"sudo apt-get install openssh-client openssh-server"
# Download libserf source code .
"serf-1.3.2.tar.bz2"
D. libserf 需要 scons 來編譯, 所以只好下載 scons-local-2.3.0.tar.gz 來用.
新版的 SVN (好像 1.7 以上) 就不會有這樣的狀況了 ,
只有根目錄建立一個.svn folder , 這樣有些好處, 我常常找一個接近的應用碼, 然後將整個目錄複製成另外一個名稱,然後開始新的程式, 最後要 commit 的時候就出現大問題的,因為我也複製的一個 .svn 資料夾,導致所有 svn 動作怪怪的 !! 新版就不會有這樣的問題了..
另外,可能不需要到處尋找.svn 資料夾, 所以 svn st 感覺好像有快一點....
不過要再 ubuntu 12.04LTS 上裝 SVN version 1.8.4就會出現一堆問題了 @@ .
我的習慣都是利用 source code 在環境上 make ; make install 的方式.
好吧...自己來裝...
A. Download subversion V1.8.4 source code form subversion official side.
"subversion-1.8.4.tar.gz"
B. The svn 1.8.4 need sqlite3 , so download sqlite3 source from offical side.
"sqlite-autoconf-3080100.tar.gz"
C. If the SVN need suppoer http or https , need libserf & openSSH .
# The openSSH your can install with
"sudo apt-get install openssh-client openssh-server"
# Download libserf source code .
"serf-1.3.2.tar.bz2"
D. libserf 需要 scons 來編譯, 所以只好下載 scons-local-2.3.0.tar.gz 來用.
下載完畢,可以開始動工 ,首先編譯 sqlite3 , 這邊沒有遇到問題 , 一般解開 , configure , make , make install ...就完成.
接著準備 libserf , 先將 scons 直接解開在 /usr/local/bin/ 下即可.接著解開 libserf .
在 libserf 資料夾內執行 "scons PREFIX=/usr/local" , 接著執行 "sudo scons install" 這樣又完成了, 就可以在/usr/local/lib/ 下看到 libserf..xxxxx 的 so file .
在 libserf 資料夾內執行 "scons PREFIX=/usr/local" , 接著執行 "sudo scons install" 這樣又完成了, 就可以在/usr/local/lib/ 下看到 libserf..xxxxx 的 so file .
可以開始編譯 svn 了 ,解開source code , 解開執行 ./configure --prefix=/usr/local , 接著 make , sudo make install . 就完成了 !!
檢視看看svn 吧 , 利用 svn --version 可以看到下列訊息. 支援 http & https 了.
如果沒有使用serf , 就只有 ra_svn & ra_local 兩的項目 !
如果沒有使用serf , 就只有 ra_svn & ra_local 兩的項目 !
2013年4月16日 星期二
ext4 filesystem VS stat command.
這篇文章我不知道要如何命名比較恰當 , 主要會使用 mkfs.ext4 和 stat 這兩個utility 所以就簡單命名了 !!
在 embedded system 中 nand flash space 是很寶貴的 , 不能太浪費 !!
需求來了, 我需要計算要放入的資料大小(包含一堆目錄)然後建立一個 剛剛好大小的 image file , 這個 image file 需要包含 ext4 filesystem ,量產時只要將這個 image file 直接燒錄到eMMC 對應的partition 中即可.
問題是.... 現在我每次都計算部準確 .... 常常 空間不足夠 , 又不能浪費一堆空間 (這個是純資料 , Device 會使用 mount -o r ..... read-only type , 所以浪費的也用不到 !! )
先說 stat 吧 !!
stat 可以將檔案 , 資料夾所需要的 block & size都列印出來 , 指令如下:
STAT="/bin/stat"
size=`${STAT} -c%s $1`
block=`${STAT} -c%b $1`
也可以利用 stat 來知道是檔案還是資料夾
${STAT} ${dir_list} | grep -q "directory"
if [ $? != 0 ];then
## echo "###### FileStatus ${dir_list}"
FileStatus ${dir_list}
else
## echo "###### DirectStatus ${dir_list}"
DirectStatus ${dir_list}
if [ DIRECT_COUNT != 0 ];then
let DIRECT_COUNT=DIRECT_COUNT-1
fi
fi
然後用 shell file 計算所有資料夾需要的 size 和 block count .
這邊要注意 , ext2/ext4 的 block size 有 1024/2048 & 4096 區分 .
一樣會有內部斷裂問題 (小於 block size會使用一個 block 去儲存) .
stat -c%b 回應的值是 disk 的最小block 單位 目前都是 512 bytes.
或者可以用 stat -c%B 去讀取 確定的大小值 .
我寫了一個 xstat.sh shell scrip 去計算所有資料的大小 , 如下:
example :
[jeff@instant ec910-sdk]$ ./build/xstat.sh src/
file count:145
folder count:7
file size:2055196
folder size:94208
total need block:603
[jeff@instant ec910-sdk]$
這個 shell 統計出 需要 603 個 block 也就是需要 603 * 4096 bytes.
ext4 filesystem structure:
在 embedded system 中 nand flash space 是很寶貴的 , 不能太浪費 !!
需求來了, 我需要計算要放入的資料大小(包含一堆目錄)然後建立一個 剛剛好大小的 image file , 這個 image file 需要包含 ext4 filesystem ,量產時只要將這個 image file 直接燒錄到eMMC 對應的partition 中即可.
問題是.... 現在我每次都計算部準確 .... 常常 空間不足夠 , 又不能浪費一堆空間 (這個是純資料 , Device 會使用 mount -o r ..... read-only type , 所以浪費的也用不到 !! )
先說 stat 吧 !!
stat 可以將檔案 , 資料夾所需要的 block & size都列印出來 , 指令如下:
STAT="/bin/stat"
size=`${STAT} -c%s $1`
block=`${STAT} -c%b $1`
也可以利用 stat 來知道是檔案還是資料夾
${STAT} ${dir_list} | grep -q "directory"
if [ $? != 0 ];then
## echo "###### FileStatus ${dir_list}"
FileStatus ${dir_list}
else
## echo "###### DirectStatus ${dir_list}"
DirectStatus ${dir_list}
if [ DIRECT_COUNT != 0 ];then
let DIRECT_COUNT=DIRECT_COUNT-1
fi
fi
然後用 shell file 計算所有資料夾需要的 size 和 block count .
這邊要注意 , ext2/ext4 的 block size 有 1024/2048 & 4096 區分 .
一樣會有內部斷裂問題 (小於 block size會使用一個 block 去儲存) .
stat -c%b 回應的值是 disk 的最小block 單位 目前都是 512 bytes.
或者可以用 stat -c%B 去讀取 確定的大小值 .
我寫了一個 xstat.sh shell scrip 去計算所有資料的大小 , 如下:
example :
[jeff@instant ec910-sdk]$ ./build/xstat.sh src/
file count:145
folder count:7
file size:2055196
folder size:94208
total need block:603
[jeff@instant ec910-sdk]$
這個 shell 統計出 需要 603 個 block 也就是需要 603 * 4096 bytes.
ext4 filesystem structure:
shell script 中的 getopt & getopts
在 linux shell 中我們常常用到 傳入一些參數 . 之前我都使用 getopts 的方式, 使用上都好好的 , example file 如下(片段):
while getopts ":O:h" opt; do
echo "opt:${opt}"
case ${opt} in
## ---- output size setting.
O )
OUT_BLOCK_SZ=${OPTARG}
;;
## ---- help menu.
\? | h )
${MKMATE} color -fred "## Bad command ${OPTARG}...."
Usage
return 1
;;
esac
done
今天想要輸入 一堆其它的值 例如: <$> foo.sh -O 1234 dir_1 dir_2 dir_3. or
<$> foo.sh dir_1 dir_2 dir_3 ..... -O 1234
使用之前就的方式卻出問題, 這些 dir 訊息沒有辦法取出(maybe I got some mistake in getopts ),所以就換成使用 getopt 了, example 如下:
args=`getopt O:h $@ `
if [ $? != 0 ];then
${MKMATE} color -fred "## Bad command ${OPTARG}...."
return 1
fi
set -- ${args}
while [ -n "$1" ]
do
case $1 in
## ---- output size setting.
-O )
shift
OUT_BLOCK_SZ=$1
;;
-h | - )
Usage
return 1
;;
\? )
${MKMATE} color -fred "## Bad command ${OPTARG}...."
Usage
return 1
;;
-- )
DIR_LIST=
;;
* )
if [ -z ${DIR_LIST} ];then
DIR_LIST="$1"
else
DIR_LIST=${DIR_LIST}:$1
fi
;;
esac
shift
done
不管輸入方式是 <$>foo.sh -O 1234 dir_1 ..... or <$>foo.sh dir_1 ..... -O 1234
最後 srgs 都會 -O 1234 -- dir_1 dir_2 .....
接著用 set -- ${args} 就可以重新設定 $@ 參數 .
在利用 while 和 shift 就可以逐一取出想要的參數 , 利用 -- 當區隔就可以將 dir_1 dir_2 取入 .
我利用 : 將這些 dir 資料並起來, 有利於我的處理需求 !!
Reference :
http://changyy.pixnet.net/blog/post/25660234-%5Bbash%5D-%E5%B0%87%E6%AA%94%E6%A1%88%E5%85%A7%E7%9A%84%E5%AD%97%E4%B8%B2%E9%80%B2%E8%A1%8C%E5%8F%96%E4%BB%A3
while getopts ":O:h" opt; do
echo "opt:${opt}"
case ${opt} in
## ---- output size setting.
O )
OUT_BLOCK_SZ=${OPTARG}
;;
## ---- help menu.
\? | h )
${MKMATE} color -fred "## Bad command ${OPTARG}...."
Usage
return 1
;;
esac
done
今天想要輸入 一堆其它的值 例如: <$> foo.sh -O 1234 dir_1 dir_2 dir_3. or
<$> foo.sh dir_1 dir_2 dir_3 ..... -O 1234
使用之前就的方式卻出問題, 這些 dir 訊息沒有辦法取出(maybe I got some mistake in getopts ),所以就換成使用 getopt 了, example 如下:
args=`getopt O:h $@ `
if [ $? != 0 ];then
${MKMATE} color -fred "## Bad command ${OPTARG}...."
return 1
fi
set -- ${args}
while [ -n "$1" ]
do
case $1 in
## ---- output size setting.
-O )
shift
OUT_BLOCK_SZ=$1
;;
-h | - )
Usage
return 1
;;
\? )
${MKMATE} color -fred "## Bad command ${OPTARG}...."
Usage
return 1
;;
-- )
DIR_LIST=
;;
* )
if [ -z ${DIR_LIST} ];then
DIR_LIST="$1"
else
DIR_LIST=${DIR_LIST}:$1
fi
;;
esac
shift
done
不管輸入方式是 <$>foo.sh -O 1234 dir_1 ..... or <$>foo.sh dir_1 ..... -O 1234
最後 srgs 都會 -O 1234 -- dir_1 dir_2 .....
接著用 set -- ${args} 就可以重新設定 $@ 參數 .
在利用 while 和 shift 就可以逐一取出想要的參數 , 利用 -- 當區隔就可以將 dir_1 dir_2 取入 .
我利用 : 將這些 dir 資料並起來, 有利於我的處理需求 !!
Reference :
http://changyy.pixnet.net/blog/post/25660234-%5Bbash%5D-%E5%B0%87%E6%AA%94%E6%A1%88%E5%85%A7%E7%9A%84%E5%AD%97%E4%B8%B2%E9%80%B2%E8%A1%8C%E5%8F%96%E4%BB%A3
2013年2月20日 星期三
auto mount NAS direct on /home/
最近因為需要處理NAS 上的資料 , 每天早上開機都要 mount 一次 , 麻煩 , 寫一個 shell 來自動 mount 吧 !!
開機完畢後只要執行這個 shell 就會將需要的 mount 好. 預設值是 Public & Software 資料夾 , mount 位置在 /home/${USER}/NAS/ , 並且會自動建立對應位置 , 如果需要額外資料夾 , 可以 使用 nas_mount.sh abc 的方式.
這個 shell 會檢查 NAS 是否在線上 (ping IP) .
也會自動檢查是否已經 mount 好了.
內容如下:
#!/bin/sh
#
# vi:set ts=4:
## ===== defind echo color
COLOR_ECHO="echo -e" ## in Fedora the color echo need have -e argument.
COLOR_NORMAL="\033[0m"
COLOR_BOLD="\033[1m"
COLOR_RED="\033[31m"
COLOR_GREEN="\033[32m"
COLOR_YELLOW="\033[33m"
COLOR_BLUE="\033[34m"
## =====================================
## NAS Mount function.
## =====================================
function NAS_mount()
{
nas_dir="$1"
mnt_dir="$2"
## ---- echo some message .
${COLOR_ECHO} "${COLOR_BOLD}""${COLOR_BLUE}" \
"## Mount NAS \"${nas_dir}\" director on \"${mnt_dir}\" ........" \
"${COLOR_NORMAL}"
## ---- check mnt_dir exist
if [ ! -e "${mnt_dir}" ];then
mkdir -p ${mnt_dir}
fi
## ---- check already mounted
_mounted=`cat /proc/mounts | grep NAS/${nas_dir} | awk '{print $2}'`
_mount_src=`cat /proc/mounts | grep NAS/${nas_dir} | awk '{print $1}'`
if [ -n "${_mounted}" ];then
${COLOR_ECHO} "${COLOR_BOLD}""${COLOR_GREEN}" \
" # NAS \"${_mount_src}\" Already mount on \"${mnt_dir}\" ......" \
"${COLOR_NORMAL}"
else
_nas_aliv=`ping ${NAS_IP} -c 1 | grep errors | sed 's/.*errors/errors/g' | sed 's/errors.*/errors/g' `
if [ -z "${_nas_aliv}" ];then
echo xxxxxxx | su -c " mount.cifs //192.168.2.6/${nas_dir} ${mnt_dir} -o user=jeff -o pass=xxxxxxxxx" > /dev/null 2>&1
else
${COLOR_ECHO} "${COLOR_BOLD}""${COLOR_RED}" \
"NAS Server not ready \"${NAS_IP}\" ........" \
"${COLOR_NORMAL}"
fi
fi
}
## ===== Mount NAS on System.
NAS_IP="192.168.2.6"
if [ -z "$1" ];then
NAS_mount "Software" "/home/${USER}/NAS/Software"
NAS_mount "Public" "/home/${USER}/NAS/Public"
else
NAS_mount "${1}" "/home/${USER}/NAS/${1}"
fi
## =================
開機完畢後只要執行這個 shell 就會將需要的 mount 好. 預設值是 Public & Software 資料夾 , mount 位置在 /home/${USER}/NAS/ , 並且會自動建立對應位置 , 如果需要額外資料夾 , 可以 使用 nas_mount.sh abc 的方式.
這個 shell 會檢查 NAS 是否在線上 (ping IP) .
也會自動檢查是否已經 mount 好了.
內容如下:
#!/bin/sh
#
# vi:set ts=4:
## ===== defind echo color
COLOR_ECHO="echo -e" ## in Fedora the color echo need have -e argument.
COLOR_NORMAL="\033[0m"
COLOR_BOLD="\033[1m"
COLOR_RED="\033[31m"
COLOR_GREEN="\033[32m"
COLOR_YELLOW="\033[33m"
COLOR_BLUE="\033[34m"
## =====================================
## NAS Mount function.
## =====================================
function NAS_mount()
{
nas_dir="$1"
mnt_dir="$2"
## ---- echo some message .
${COLOR_ECHO} "${COLOR_BOLD}""${COLOR_BLUE}" \
"## Mount NAS \"${nas_dir}\" director on \"${mnt_dir}\" ........" \
"${COLOR_NORMAL}"
## ---- check mnt_dir exist
if [ ! -e "${mnt_dir}" ];then
mkdir -p ${mnt_dir}
fi
## ---- check already mounted
_mounted=`cat /proc/mounts | grep NAS/${nas_dir} | awk '{print $2}'`
_mount_src=`cat /proc/mounts | grep NAS/${nas_dir} | awk '{print $1}'`
if [ -n "${_mounted}" ];then
${COLOR_ECHO} "${COLOR_BOLD}""${COLOR_GREEN}" \
" # NAS \"${_mount_src}\" Already mount on \"${mnt_dir}\" ......" \
"${COLOR_NORMAL}"
else
_nas_aliv=`ping ${NAS_IP} -c 1 | grep errors | sed 's/.*errors/errors/g' | sed 's/errors.*/errors/g' `
if [ -z "${_nas_aliv}" ];then
echo xxxxxxx | su -c " mount.cifs //192.168.2.6/${nas_dir} ${mnt_dir} -o user=jeff -o pass=xxxxxxxxx" > /dev/null 2>&1
else
${COLOR_ECHO} "${COLOR_BOLD}""${COLOR_RED}" \
"NAS Server not ready \"${NAS_IP}\" ........" \
"${COLOR_NORMAL}"
fi
fi
}
## ===== Mount NAS on System.
NAS_IP="192.168.2.6"
if [ -z "$1" ];then
NAS_mount "Software" "/home/${USER}/NAS/Software"
NAS_mount "Public" "/home/${USER}/NAS/Public"
else
NAS_mount "${1}" "/home/${USER}/NAS/${1}"
fi
## =================
2013年2月18日 星期一
用 shell + crontab 固定發出 openkm report Mail.
最近因為 上級要知道 OpenKM(簡稱 OKM) 內容的更動狀況 , 需要每週 report 一次 !!
主要是看看這星期 OKM 內容有哪些人更動!!
Google 一下 有關 OKM 的 auto report , 發現需要會 java script , 我不會 @@ !!
想說 , OKM 的 dashboard 可以看到 log 內容 , 應該 找一下 log file , 也會有吧 !!
找了半天 , 發現 /openkm-6.2.2-community/tomcat/repository/okmdb.log 內有我要的訊息 , 好吧 , 山不轉 人轉 , 寫一個 shell script + crontab 來執行 auto report 吧 !!
首先寫一個 shell , 由 okmdb.log 內容取出 document create/delete & check in 的項目
到一個 temp file , shell 如下:
#!/bin/sh
#
# vi:set ts=4:
#############################
## ===== defind echo color
#############################
COLOR_ECHO="echo -e" ## in Fedora the color echo need have -e argument.
COLOR_NORMAL="\033[0m"
COLOR_BOLD="\033[1m"
COLOR_RED="\033[31m"
COLOR_GREEN="\033[32m"
COLOR_YELLOW="\033[33m"
COLOR_BLUE="\033[34m"
#############################
## ==== check log file exist.
#############################
OKMDB_LOG="/home/jeff/openkm-6.2.2-community/tomcat/repository/okmdb.log"
TEMP_FILE="/home/jeff/okm_report.txt"
if [ ! -e ${OKMDB_LOG} ];then
${COLOR_ECHO} "${COLOR_BOLD}""${COLOR_RED}" \
"The log file \"${OKMDB_LOG}\" not exist !! " \
"${COLOR_NORMAL}"
exit
fi
#############################
## ==== Crate title
#############################
echo -e "ITEM\t\t\tDATE\t\t\tFILES\t\t\tUSER" > ${TEMP_FILE}
echo >> ${TEMP_FILE}
#############################
## ==== get document create event log.
#############################
DOC_CREATE=`cat ${OKMDB_LOG} | grep "CREATE_DOCUMENT" | sed 's/\ /:/g'`
IFS="
"
for list in ${DOC_CREATE} ; do
IFS=','
_date=`echo ${list} | awk '{print $3}' | sed 's/\..*//g' | sed 's/'\''//g' `
_file=`echo ${list} | awk '{print $7}' | sed 's/'\''//g' | sed 's/\/okm\:root\///g' `
_user=`echo ${list} | awk '{print $8}' | sed 's/'\''//g' | sed 's/)//g'`
echo -e "CREATE\t\t${_date}\t\t${_file}\t\t${_user}" >> ${TEMP_FILE}
done
echo >> ${TEMP_FILE}
#############################
## ==== get document delete event log.
#############################
DOC_DELETE=`cat ${OKMDB_LOG} | grep "DELETE_DOCUMENT" | sed 's/\ /:/g'`
IFS="
"
for list in ${DOC_DELETE} ; do
IFS=','
_date=`echo ${list} | awk '{print $3}' | sed 's/\..*//g' | sed 's/'\''//g' `
_file=`echo ${list} | awk '{print $6}' | sed 's/'\''//g' | sed 's/\/okm\:root\///g' `
_user=`echo ${list} | awk '{print $7}' | sed 's/'\''//g' | sed 's/)//g'`
echo -e "DELETE\t\t${_date}\t\t${_file}\t\t${_user}" >> ${TEMP_FILE}
done
echo >> ${TEMP_FILE}
#############################
## ==== get document modify event log.
#############################
DOC_CHECKIN=`cat ${OKMDB_LOG} | grep "CHECKIN_DOCUMENT" | sed 's/\ /:/g'`
IFS="
"
for list in ${DOC_CHECKIN} ; do
IFS=','
_date=`echo ${list} | awk '{print $3}' | sed 's/\..*//g' | sed 's/'\''//g' `
_file=`echo ${list} | awk '{print $7}' | sed 's/'\''//g' | sed 's/\/okm\:root\///g' `
_user=`echo ${list} | awk '{print $8}' | sed 's/'\''//g' | sed 's/)//g'`
echo -e "CHECKIN\t\t${_date}\t\t${_file}\t\t${_user}" >> ${TEMP_FILE}
done
echo >> ${TEMP_FILE}
##############################
## ==== send notify mail
#############################
NOTIFY_MAIL="jeff@xxxxxxx.com.tw,ch@xxxxxxx.com.tw,emiliehuang@xxxxxxx.com.tw,alvin@xxxxxxx.com.tw,pp@xxxxxxx.com.tw"
#NOTIFY_MAIL="jeff@xxxxxxx.com.tw"
mail -s "Content report" ${NOTIFY_MAIL} < ${TEMP_FILE}
rm -rf ${TEMP_FILE}
## ==== remove log file ??
if [ ! -z "$1" ] && [ "$1" == "-d" ];then
echo > ${OKMDB_LOG}
fi
#############################
接著 , 我不想每天(星期)都去檢查 report file , 就將這個 file 內容直接mail 給上級吧^^ !!
要讓 fedora 發 mail 其實蠻簡單的 用 "mail -s "Content report" ${NOTIFY_MAIL} < ${TEMP_FILE}"
就可以了!!
不過要先設定 smtp 的內容 , google 一下 其實也不難 , 只要建立 /home/jeff/.mailrc 檔案, 且.mailrc 內容填入下列即可:
set from=Server_AUTO<jeff@xxxxxx.com.tw> smtp=xxxx.com.tw
set smtp-auth-user=jeff@xxxx.com.tw smtp-auth-password=*******
set smtp-auth=login
上面有些個人訊息 , 就用 xxxx 和 **** 代替 !!
收到 mail 的內容就會如下:
ITEM DATE FILES USER
CREATE 2013-02-06:10:28:40 share/data_sip_image_ec510/sip/Image/English/Lower/btn_backspace_press.png alvin
CREATE 2013-02-06:10:29:18 share/data_sip_image_ec510/sip/Image/English/Upper/btn_backspace_press.png alvin..........
主要是看看這星期 OKM 內容有哪些人更動!!
Google 一下 有關 OKM 的 auto report , 發現需要會 java script , 我不會 @@ !!
想說 , OKM 的 dashboard 可以看到 log 內容 , 應該 找一下 log file , 也會有吧 !!
找了半天 , 發現 /openkm-6.2.2-community/tomcat/repository/okmdb.log 內有我要的訊息 , 好吧 , 山不轉 人轉 , 寫一個 shell script + crontab 來執行 auto report 吧 !!
首先寫一個 shell , 由 okmdb.log 內容取出 document create/delete & check in 的項目
到一個 temp file , shell 如下:
#!/bin/sh
#
# vi:set ts=4:
#############################
## ===== defind echo color
#############################
COLOR_ECHO="echo -e" ## in Fedora the color echo need have -e argument.
COLOR_NORMAL="\033[0m"
COLOR_BOLD="\033[1m"
COLOR_RED="\033[31m"
COLOR_GREEN="\033[32m"
COLOR_YELLOW="\033[33m"
COLOR_BLUE="\033[34m"
#############################
## ==== check log file exist.
#############################
OKMDB_LOG="/home/jeff/openkm-6.2.2-community/tomcat/repository/okmdb.log"
TEMP_FILE="/home/jeff/okm_report.txt"
if [ ! -e ${OKMDB_LOG} ];then
${COLOR_ECHO} "${COLOR_BOLD}""${COLOR_RED}" \
"The log file \"${OKMDB_LOG}\" not exist !! " \
"${COLOR_NORMAL}"
exit
fi
#############################
## ==== Crate title
#############################
echo -e "ITEM\t\t\tDATE\t\t\tFILES\t\t\tUSER" > ${TEMP_FILE}
echo >> ${TEMP_FILE}
#############################
## ==== get document create event log.
#############################
DOC_CREATE=`cat ${OKMDB_LOG} | grep "CREATE_DOCUMENT" | sed 's/\ /:/g'`
IFS="
"
for list in ${DOC_CREATE} ; do
IFS=','
_date=`echo ${list} | awk '{print $3}' | sed 's/\..*//g' | sed 's/'\''//g' `
_file=`echo ${list} | awk '{print $7}' | sed 's/'\''//g' | sed 's/\/okm\:root\///g' `
_user=`echo ${list} | awk '{print $8}' | sed 's/'\''//g' | sed 's/)//g'`
echo -e "CREATE\t\t${_date}\t\t${_file}\t\t${_user}" >> ${TEMP_FILE}
done
echo >> ${TEMP_FILE}
#############################
## ==== get document delete event log.
#############################
DOC_DELETE=`cat ${OKMDB_LOG} | grep "DELETE_DOCUMENT" | sed 's/\ /:/g'`
IFS="
"
for list in ${DOC_DELETE} ; do
IFS=','
_date=`echo ${list} | awk '{print $3}' | sed 's/\..*//g' | sed 's/'\''//g' `
_file=`echo ${list} | awk '{print $6}' | sed 's/'\''//g' | sed 's/\/okm\:root\///g' `
_user=`echo ${list} | awk '{print $7}' | sed 's/'\''//g' | sed 's/)//g'`
echo -e "DELETE\t\t${_date}\t\t${_file}\t\t${_user}" >> ${TEMP_FILE}
done
echo >> ${TEMP_FILE}
#############################
## ==== get document modify event log.
#############################
DOC_CHECKIN=`cat ${OKMDB_LOG} | grep "CHECKIN_DOCUMENT" | sed 's/\ /:/g'`
IFS="
"
for list in ${DOC_CHECKIN} ; do
IFS=','
_date=`echo ${list} | awk '{print $3}' | sed 's/\..*//g' | sed 's/'\''//g' `
_file=`echo ${list} | awk '{print $7}' | sed 's/'\''//g' | sed 's/\/okm\:root\///g' `
_user=`echo ${list} | awk '{print $8}' | sed 's/'\''//g' | sed 's/)//g'`
echo -e "CHECKIN\t\t${_date}\t\t${_file}\t\t${_user}" >> ${TEMP_FILE}
done
echo >> ${TEMP_FILE}
##############################
## ==== send notify mail
#############################
NOTIFY_MAIL="jeff@xxxxxxx.com.tw,ch@xxxxxxx.com.tw,emiliehuang@xxxxxxx.com.tw,alvin@xxxxxxx.com.tw,pp@xxxxxxx.com.tw"
#NOTIFY_MAIL="jeff@xxxxxxx.com.tw"
mail -s "Content report" ${NOTIFY_MAIL} < ${TEMP_FILE}
rm -rf ${TEMP_FILE}
## ==== remove log file ??
if [ ! -z "$1" ] && [ "$1" == "-d" ];then
echo > ${OKMDB_LOG}
fi
#############################
接著 , 我不想每天(星期)都去檢查 report file , 就將這個 file 內容直接mail 給上級吧^^ !!
要讓 fedora 發 mail 其實蠻簡單的 用 "mail -s "Content report" ${NOTIFY_MAIL} < ${TEMP_FILE}"
就可以了!!
不過要先設定 smtp 的內容 , google 一下 其實也不難 , 只要建立 /home/jeff/.mailrc 檔案, 且.mailrc 內容填入下列即可:
set from=Server_AUTO<jeff@xxxxxx.com.tw> smtp=xxxx.com.tw
set smtp-auth-user=jeff@xxxx.com.tw smtp-auth-password=*******
set smtp-auth=login
上面有些個人訊息 , 就用 xxxx 和 **** 代替 !!
收到 mail 的內容就會如下:
ITEM DATE FILES USER
CREATE 2013-02-06:10:28:40 share/data_sip_image_ec510/sip/Image/English/Lower/btn_backspace_press.png alvin
CREATE 2013-02-06:10:29:18 share/data_sip_image_ec510/sip/Image/English/Upper/btn_backspace_press.png alvin..........
2012年9月26日 星期三
自動執行 備份 part 2
前一篇有提到我還需要備份 字典的 Content , 如法泡製 , 相關內容如下:
先用 crontab 增加一項目:
crontab -l 的內容如下: (擷取 相關的 )
0 23 * * 5 /home/jeff/bin/backup-content.sh > /home/jeff/backup-content.txt 2>&1
相對的 ,寫一個 shell file . 如下:
動作和 之前 SVN 備份差不多 , 只是這次直接對 NAS 上 進行備份 , 並且使用 7z 壓縮程式 !! (tar 好像超過 4G 會有問題 ) .
#!/bin/sh
#
# vi:set ts=4:
## ===== defind echo color
COLOR_ECHO="echo -e" ## in Fedora the color echo need have -e argument.
COLOR_NORMAL="\033[0m"
COLOR_BOLD="\033[1m"
COLOR_RED="\033[31m"
COLOR_GREEN="\033[32m"
COLOR_YELLOW="\033[33m"
## ===== define source and distnation dir .
BACKUP_SRC="/home/${USER}/NAS/Project/Content"
BACKUP_DEST="/home/${USER}/NAS/Project/backup"
NAS_KEEP_ITEM=12
################################################
#### check log file size.
################################################
LOG_MAX_SZ=`echo "1048576 * 100" | bc -l `
LOG_FILE=/home/${USER}/backup-content.log
TXT_FILE=/home/${USER}/backup-content.txt
if [ -e ${LOG_FILE} ];then
_file_sz=`ls -l ${LOG_FILE} | awk '{print $5}'`
if [ ${_file_sz} -gt ${LOG_MAX_SZ} ];then
rm ${LOG_FILE}
fi
fi
################################################
#### chekc NAS direct
################################################
if [ ! -e /home/${USER}/NAS ];then
${COLOR_ECHO} "${COLOR_BOLD}""${COLOR_RED}""#### NAS file system not exist !! ""${COLOR_NORMAL}"
exit 1
elif [ ! -e ${BACKUP_DEST} ];then
${COLOR_ECHO} "${COLOR_BOLD}""${COLOR_RED}""#### backup dir not exist !! ""${COLOR_NORMAL}"
exit 1
fi
#### Check NAS content source dir
if [ ! -e ${BACKUP_SRC} ];then
${COLOR_ECHO} "${COLOR_BOLD}""${COLOR_RED}""#### Content source dir not exist !! ""${COLOR_NORMAL}"
exit 1
fi
################################################
#### Check 7z tools
################################################
COMPRESS_7Z="/usr/local/bin/7z"
VER_7Z=`${COMPRESS_7Z} | grep Copyright | sed 's/Copyright.*//g' | awk '{print $2}'`
## echo "VER_7Z:${VER_7Z}"
## echo "COMPRESS_7Z:${COMPRESS_7Z}"
if [ -z "${VER_7Z}" ];then
${COLOR_ECHO} "${COLOR_BOLD}""${COLOR_RED}""#### Compress Tool 7Z not found !! ""${COLOR_NORMAL}"
exit 1
else
${COLOR_ECHO} "${COLOR_BOLD}""${COLOR_GREEN}""#### 7Z Version is ${VER_7Z} . ""${COLOR_NORMAL}"
fi
################################################
#### Start back up
################################################
_cur_date=`date +%Y_%m_%d`
_bak_name=${BACKUP_DEST}/Content_${_cur_date}.7z
${COLOR_ECHO} "${COLOR_BOLD}""${COLOR_GREEN}""#### Strating Compress ${BACKUP_SRC} ...... ""${COLOR_NORMAL}"
## echo "***************"" 7z a ${_bak_name} ${BACKUP_SRC} "
${COMPRESS_7Z} a ${_bak_name} ${BACKUP_SRC}
################################################
#### Remove oldset back up files .
################################################
## rm oldest back up file
_lists=`find ${BACKUP_DEST}/* | xargs ls -t | grep -n ".7z"`
## echo "_lists:${_lists}"
for _del_item in ${_lists} ; do
_list_id=`echo ${_del_item} | sed 's/:.*//g' `
_item_name=`echo ${_del_item} | sed 's/.*://g' `
if [ ${_list_id} -gt ${NAS_KEEP_ITEM} ];then
## echo "***************"" rm -vf ${_item_name} "
rm -vf ${_item_name}
fi
done
################################################
if [ -e ${TXT_FILE} ];then
cat ${TXT_FILE} >> ${LOG_FILE}
fi
################################################
先用 crontab 增加一項目:
crontab -l 的內容如下: (擷取 相關的 )
0 23 * * 5 /home/jeff/bin/backup-content.sh > /home/jeff/backup-content.txt 2>&1
相對的 ,寫一個 shell file . 如下:
動作和 之前 SVN 備份差不多 , 只是這次直接對 NAS 上 進行備份 , 並且使用 7z 壓縮程式 !! (tar 好像超過 4G 會有問題 ) .
#!/bin/sh
#
# vi:set ts=4:
## ===== defind echo color
COLOR_ECHO="echo -e" ## in Fedora the color echo need have -e argument.
COLOR_NORMAL="\033[0m"
COLOR_BOLD="\033[1m"
COLOR_RED="\033[31m"
COLOR_GREEN="\033[32m"
COLOR_YELLOW="\033[33m"
## ===== define source and distnation dir .
BACKUP_SRC="/home/${USER}/NAS/Project/Content"
BACKUP_DEST="/home/${USER}/NAS/Project/backup"
NAS_KEEP_ITEM=12
################################################
#### check log file size.
################################################
LOG_MAX_SZ=`echo "1048576 * 100" | bc -l `
LOG_FILE=/home/${USER}/backup-content.log
TXT_FILE=/home/${USER}/backup-content.txt
if [ -e ${LOG_FILE} ];then
_file_sz=`ls -l ${LOG_FILE} | awk '{print $5}'`
if [ ${_file_sz} -gt ${LOG_MAX_SZ} ];then
rm ${LOG_FILE}
fi
fi
################################################
#### chekc NAS direct
################################################
if [ ! -e /home/${USER}/NAS ];then
${COLOR_ECHO} "${COLOR_BOLD}""${COLOR_RED}""#### NAS file system not exist !! ""${COLOR_NORMAL}"
exit 1
elif [ ! -e ${BACKUP_DEST} ];then
${COLOR_ECHO} "${COLOR_BOLD}""${COLOR_RED}""#### backup dir not exist !! ""${COLOR_NORMAL}"
exit 1
fi
#### Check NAS content source dir
if [ ! -e ${BACKUP_SRC} ];then
${COLOR_ECHO} "${COLOR_BOLD}""${COLOR_RED}""#### Content source dir not exist !! ""${COLOR_NORMAL}"
exit 1
fi
################################################
#### Check 7z tools
################################################
COMPRESS_7Z="/usr/local/bin/7z"
VER_7Z=`${COMPRESS_7Z} | grep Copyright | sed 's/Copyright.*//g' | awk '{print $2}'`
## echo "VER_7Z:${VER_7Z}"
## echo "COMPRESS_7Z:${COMPRESS_7Z}"
if [ -z "${VER_7Z}" ];then
${COLOR_ECHO} "${COLOR_BOLD}""${COLOR_RED}""#### Compress Tool 7Z not found !! ""${COLOR_NORMAL}"
exit 1
else
${COLOR_ECHO} "${COLOR_BOLD}""${COLOR_GREEN}""#### 7Z Version is ${VER_7Z} . ""${COLOR_NORMAL}"
fi
################################################
#### Start back up
################################################
_cur_date=`date +%Y_%m_%d`
_bak_name=${BACKUP_DEST}/Content_${_cur_date}.7z
${COLOR_ECHO} "${COLOR_BOLD}""${COLOR_GREEN}""#### Strating Compress ${BACKUP_SRC} ...... ""${COLOR_NORMAL}"
## echo "***************"" 7z a ${_bak_name} ${BACKUP_SRC} "
${COMPRESS_7Z} a ${_bak_name} ${BACKUP_SRC}
################################################
#### Remove oldset back up files .
################################################
## rm oldest back up file
_lists=`find ${BACKUP_DEST}/* | xargs ls -t | grep -n ".7z"`
## echo "_lists:${_lists}"
for _del_item in ${_lists} ; do
_list_id=`echo ${_del_item} | sed 's/:.*//g' `
_item_name=`echo ${_del_item} | sed 's/.*://g' `
if [ ${_list_id} -gt ${NAS_KEEP_ITEM} ];then
## echo "***************"" rm -vf ${_item_name} "
rm -vf ${_item_name}
fi
done
################################################
if [ -e ${TXT_FILE} ];then
cat ${TXT_FILE} >> ${LOG_FILE}
fi
################################################
訂閱:
文章 (Atom)