2013年12月15日 星期日

iOS 開發筆記 - UITableViewController Editing Mode 隱藏 Delete 按鈕(Move Only)

UITableViewControllerEditMode

開始把玩 UITableViewController Editing mode,由於不想讓使用者刪資料,所以就需要把 Delete 相關的操作介面給關掉。

簡言之:

- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
    //if (self.editing)
    //    return UITableViewCellEditingStyleDelete;
 
    return UITableViewCellEditingStyleNone;
}


如此一來即可:

UITableViewControllerEditModeWithoutDeletition

2013年12月14日 星期六

免費導航王 - 台灣版小米機可免費使用勤崴導航王 android app 一整年

導航王小米版

半年前幫家人添購一台小米2S,最近我自己也用了一台,然後莫名又有一台紅米。當我徜徉於紅米的高 CP 時,看到台灣小米機又可以免費用「導航王」!有更賺到的感覺!(台灣版小米2S或紅米都可以使用)

紅米機的性能的確已經非常夠用,特別適用那些常用來打電話,但偶爾又想透過手持裝置獲得瀏覽網頁資訊的人。有了免費的導航王後,紅米又可以變身為導航車機啦!

詳情請參閱:歡慶【導航王】成為小米台灣獨家導航軟體合作夥伴!

用法:

  1. 從 勤崴官網 下載專屬於台版小米機的 導航王 app (NaviKing_Android_XiaoMi_WXGA_2.134.0.116.apk)
  2. 透過 USB 傳輸線,把下載好的 NaviKing_Android_XiaoMi_WXGA_2.134.0.116.apk 擺在小米機上,如 Download 資料夾
  3. 使用小米機內建檔案管理,開啓 NaviKing_Android_XiaoMi_WXGA_2.134.0.116.apk 即可進行安裝
  4. 安裝完導航王後,啟動後再下載圖資
  5. 一切安裝完後,重新啟動導航王即可

此外,試用了導航王後,發現他已經把一些常用的旅遊、生活類功能都做進去了!不見得要把它當行車導航的 app :

導航王小米版1

導航王小米版2

導航王小米版3

導航王小米版4

導航王小米版5

2013年12月11日 星期三

紅米機開箱文

紅米開箱

不知不覺手邊就多了這隻,預計要拿來送給長輩使用的 XD 其次是當開發機。

用紅米玩一下 Candy Crush 時,發現寶石落下時會 lag ?! 有擔心一下,但是改玩 Angry Birds 倒還好,其他拍照的顏色也還滿鮮豔的。整體上品質還挺不錯的,螢幕尺寸大也挺顧眼睛的,紅米顯而易見的缺點是內建儲存容量僅4GB,可以用的可能只有2GB,還好可以加microSD來使用。

剛好手邊有小米2S就順便比一下吧!(左邊小米2S,右邊紅米)


紅米與小米2s大小差異2

紅米與小米2s大小差異

小米2s和紅米

紅米與小米2s大小差異3

試用完紅米後,我還是喜歡小米2S的尺寸 XDD 但如果是愛帶包包的人,或許就沒差別了。

iOS 開發筆記 - 使用 Core Graphics 進行文字繪圖輸出並設定 NSString Attributes (Font、Alignment) 等

draw string

整理一年多前的程式碼,發現用的函數已經在 iOS 7 宣告為 deprecated 啦,恰好重看 CS193P 時,得知 NSAttributedString 這東西,以為是最新出來的,沒想到這在 iOS 3.2 就存在啦。

順便把一些筆記都記下吧:

+ (UIImage *)buildImageAndDrawText:(NSString *)text size:(CGSize)size ios7:(BOOL)iOS7SDK
{
    UIGraphicsBeginImageContext(size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetRGBFillColor(context, 1, 1, 1, 1);
    CGContextFillRect(context, CGRectMake(0, 0, size.width, size.height));

    UIFont *font = [UIFont italicSystemFontOfSize:15];
    UIColor *fontColor = [UIColor blueColor];

    if (iOS7SDK) {
        NSMutableParagraphStyle *paragrapStyle = [[NSMutableParagraphStyle alloc] init];
        [paragrapStyle setLineBreakMode:NSLineBreakByWordWrapping];
        [paragrapStyle setAlignment:NSTextAlignmentCenter];
     
        [text drawInRect:CGRectIntegral(CGRectMake(0, size.height/2, size.width, size.height))
          withAttributes:@{
                           NSParagraphStyleAttributeName: paragrapStyle,
                           NSFontAttributeName: font,
                           NSForegroundColorAttributeName: fontColor}
         ];
    } else {
        [fontColor set];
        [text drawInRect:CGRectIntegral(CGRectMake(0, size.height/2, size.width, size.height)) withFont:font lineBreakMode:NSLineBreakByWordWrapping alignment:NSTextAlignmentCenter];
    }
 
    UIImage *outImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return outImage;
}


用法:

self.imageView = [YourClass buildImageAndDrawText:@"iOS app development @ blog.changyy.org" size:CGSizeMake(300, 300) ios7:YES];

細節可參考 Core Graphics Framework Reference


2013年12月10日 星期二

iOS 開發筆記 - 透過 semaphore 讓 function 特性從 nonblocking 回到 blocking 模式

相信從事 Window Programming 的大多都知道 Main thread(UI thread) 的特性,例如 Main Thread 用來處理事件,須儘量避免做太久的工作,此外,所謂的 nonblocking 的原理就只是在建立一支 thread 去執行任務,任務完成後在依照設計返回到 Main thread 去更動 UI 狀況。
因此,採用一些手機視窗程式等第三方 SDK 時,更是容易碰到 nonblocking functions。這會使得規劃的 function flow 有點亂掉。

在 iOS app development 環境中,以 block 為例,可以透過 semaphore 來達成 blocking 效果,如:

dispatch_semaphore_t waitJobDone = dispatch_semaphore_create(0);
[self callBlock:^{
// do something
dispatch_semaphore_signal(waitJobDone);
}];
dispatch_semaphore_wait(waitJobDone, DISPATCH_TIME_FOREVER);


順便筆記一下其他東西,隨意使用 nonblocking 用法:

dispatch_async( dispatch_queue_create(“job_running”, NULL), ^{


});


在非 UI thread 中,又想動 UI 方式:

dispatch_async( dispatch_queue_create("job_running", NULL), ^{
// do something ...
     
dispatch_async(dispatch_get_main_queue(), ^{
// update ui
});
});


最後一提,如果用到的 SDK 跟網路相關的(NSURLConnection),那大多不能用 semaphore 來使之改成 blocking 模式(或是改法很繁雜要深入or破壞SDK架構),這跟 NSURLConnection Delegate Callback 設計有關(NSRunLoop)。

iOS 開發筆記 - ARC forbids explicit message send of 'dealloc'

最近我也投入 ARC 開發模式 XD 再也不去管到底要不要對哪個 object 執行 release 的議題,這適應其實也不難,就真的不要寫 [object release] 即可 XD

但有些時候還是要在物件銷毀時去做一些處理,例如解決或等待相關的事件完成,就常常會寫這段:

- (void)dealloc
{
    // do something ...
    [super dealloc];

}


結果,Xcode 就噴這段訊息:

ARC forbids explicit message send of 'dealloc'

Transitioning to ARC Release Notes 文件查證:
You may implement a dealloc method if you need to manage resources other than releasing instance variables. You do not have to (indeed you cannot) release instance variables, but you may need to invoke [systemClassInstance setDelegate:nil] on system classes and other code that isn’t compiled using ARC. 
Custom dealloc methods in ARC do not require a call to [super dealloc] (it actually results in a compiler error). The chaining to super is automated and enforced by the compiler.

僅需改成這樣了:

- (void)dealloc
{
    // do something ...
}

iOS 開發筆記 - 使用 NSNotification 傳遞與接收事件

通常會設計多個 UIViewController 供使用者觀看與操作,如果 AUIViewController 提供設定參數後,當使用者切換到 BUIViewController 可以馬上就使用到新資料來操作時,這時可以把資料儲存在指定地方,當 BUIViewController 之 viewWillAppear 裡就能夠去重讀資料來用。

然而,有時希望 AUIViewController 改完資料時,可以馬上讓 BUIViewController 也得知,這時做法至少有兩種,第一種就是讓 AUIViewController 可以接觸到 BUIViewController ,例如把 BUIViewController 當作一個 member variable ,也就是 AUIViewController.member = BUIViewController object,只是這招有點破壞架構的美感,但偶爾使用還滿堪用的!第二種,就是採用 Notification 架構,讓 BUIViewController 聆聽專屬於它的事件,而 AUIViewController 在需要的時候送事件出去即可,此架構更加符合 async 。

此例筆記 NSNotification 的用法(此架構也稱 radio station,Android 派則是 broadcast 啦):

BUIViewController.h:

#define kTargetDataUpdate @"TargetDataUpdate"

BUIViewController.m 之註冊聆聽事件:

- (void)setupOptions:(NSNotification *)event
{
        NSLog(@"event:%@, object:%@", event, event.object);
}

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(setupOptions:) name:kTargetDataUpdate object:nil];

}


BUIViewController.m 之移除聆聽事件:

- (void)viewWillDisappear:(BOOL)animated
{
    [[NSNotificationCenter defaultCenter] removeObserver:self name: kTargetDataUpdate object:nil];
    [super viewWillDisappear:animated];
}


AUIViewController (或其他物件亦可) 之發送事件:

NSDictionary *info = @{@"status":@"ok"};
[[NSNotificationCenter defaultCenter] postNotificationName: kTargetDataUpdate object:info userInfo:nil];

2013年12月9日 星期一

iOS 開發筆記 - Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'unable to dequeue a cell with identifier myCell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard'

AttriubuteTableViewCellIndentifier DocumentOutlineTableViewCell

最近摸 Storyboard 一陣子,偶爾還會犯一些傻 bug ,就是 Identifier 設錯地方 XD 例如跑去 Identity Inspector 的 Identity 之 Restoration ID :

IndentityTableViewCellRestorationID

結果程式一跑時,就噴訊息了:

** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'unable to dequeue a cell with identifier myCell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard'

解法就是開啟 storyboard 後,仔細查看左邊的 Document Outline 之 UITableViewController 裡的 Table View Cell 是不是有被標記起來,接著去看看右邊 Attributes Inspector 顯示的 Identifier 是不是跟程式碼一樣的,如此就能 debug 啦。

忘記是不是 iOS 5 SDK 開始,預設的 UITableViewController 的範例程式都是寫成綁定在 Interface builder (xib) / Storyboard 的寫法:

static NSString *CellIdentifier = @"myCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];


早期 iOS 4 SDK 預設 UITableViewController 之 (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 寫法:

static NSString *CellIdentifier = @"myCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}


早期 Interface builder (xib) 的用法,須寫程式註冊 Nib:

- (void)viewDidLoad
{
[super viewDidLoad];
[self.tableView registerNib:[UINib nibWithNibName:@“MyCell" bundle:nil]
forCellReuseIdentifier:@“myCell”]; // "MyCell" for MyCell.xib
}

2013年12月3日 星期二

iOS 開發筆記 - 解決 Storyboard UITableViewCell contentView 第一次設定 subViews frame 失效問題

為了讓自己趕快習慣 Storyboard 開發模式,瘋狂地用 Storyboard 做事 XD 除了降低原本的開發速度外,接著碰到一些好像是 bug 的奇妙問題?

這個問題是在 Storyboard 內已經拉好一個 UITableViewController ,接著在裡頭增加 UITableViewCell 後,再上頭加多個 ImageView 把玩,然而,希望 device rotation 後能後平均地顯示在 UITableViewCell 裡頭,例如有五個 ImageView 可以平均地分布。

此時在 (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 已經完成重新計算各個 ImageView 的 frame 座標。

由於 Storyboard 是以 Portrait 模式設計,因此裝置一開始從 Portrait 轉換到 Landscape 時,一切都正常顯示。但如果裝置一開始就是在 Landscape 模式時,反而那些 ImageView 卻還是依照 Storyboard 裡的 Portrait 模式設計,無論我把他們的 frame 座標更新也無用。(但經過 device rotation 後,會顯示正常)

不曉得這是不是 bug ?總之,最後就想到一招就是第一次強制 ImageView 重新歸位 XD 希望這招之後可以拿掉。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
        // ...

        if( cell.tag == 0 )
        {
                cell.tag = 1;
                for (UIView *v in [cell.contentView subviews])
                {
                        [v removeFromSuperview];
                        [cell.contentView addSubview:v];
                }
        }
        return cell;
}

iOS 開發筆記 - iOS app 狀態為 Pending Contract 的處理

通常看到這樣的問題,都是忘了填寫銀行等資訊,不過,若發生在免費的 iOS app 時,這時就請打電話給客服聊聊天吧(Worldwide Telephone Support,台灣打 0800-022-237 )

此例是登入 iTunesConnect 後,上頭一直顯示 program membership expired 訊息,但明明我幾個月前已經 renew 過了 :P 同理切換到 Contracts, Tax, Banking 裡頭,也叫我去 re-new 開發者帳號...打電話問客服...報一下 Apple ID (Email address) 後,得知是當初 re-new 時少完成一個流程(可能是少了某表單的同意,但現況又看不到表單),導致在 iOS Developer Center 是 OK ,但在 iTunesConnect 是不 OK 的現象,解法就是聊個天就解掉了...

2013年11月29日 星期五

iOS 開發筆記 - 重溫 iOS app development


Developing iOS 7 Apps for iPhone and iPad
by Stanford

這星期一口氣連追了 12 堂課,把史丹佛 CS193P 2013 Fall 目前放出來的課程看完了 XD 距離我上一次看它可能是 2011 年,而我第一次看則是 2010 年的春天。說真的,老外上課有豐富的肢體語言,所以不必真的聽懂每一個字,看看投影片、程式碼就真的都學得會啦。

過去從 iOS 3.1 開始接觸 iPhone app development ,跟幾個同好討論怎樣把 UI 搞的炫炫的等等,當時 iOS 3 的特色大概是提出 In-App purchases,接著有幸還追到 iOS 4,帶出了 Background Tasks 跟 Objective C blocks 的概念,之後,我漸漸落伍了,如 iOS 5 提出 Storyboard 、iOS 6 帶入 iCloud 到現在的 iOS 7 等,基本上都沒有正式用過。

最近看了 CS193P 後,才真的理解 Storyboard 跟 Core Data 的好用之處,的確,沒有他們一樣可以做完事,但我相信,再過個一兩年,可能就完全跟不上潮流了

此外,想起前陣子跟新學 iOS 的聊天,被問起到底會什麼,說真的這種問題不是很好 Orz 不是不願意回答,而是答出來跟沒答出來的價值差不了多少。舉個例來說, C 跟 C99 的語法差在哪邊,知道或不知道都不會很大地影響一位工程師,同理 iOS 一直在更新,工作這種事,碰到在學就對了。至於 C 跟 C99 差別嘛,只要記得會用 Google 去查一下就好 XD

經過這次的複習,深深覺得資訊界的技術真的很難撐過兩年 XD 其中的心酸豈可不是 out of date 幾個字可道盡的。

2013年11月27日 星期三

人生中第二隻智慧手機 - 小米2s

小米2s 盒裝

扣除開發機外,第一支擁有的智慧手機是 Nexus S ,於 2011 年底買的,不幸地,它上週五離我而去。正當我思考要買哪隻手機時,iPhone 的確有引誘到我,但實在太貴了 XD 最後還是落入 CP 值的計算,窮嘛,至少要學會斤斤計較嘛。

過去 Google 原生機很誘人,可惜的是 LG Nexus 4 在台灣不怎盛行,連現在出到 LG Nexus 5 了,Google 的前兩代的 Galaxy Nexus 外頭空機還是要價一萬二起跳,所以,就直接買小米2s了,並且直接在官網買(台灣某門市賣的價錢跟官網一樣,但規格矮一階!),不等(買)紅米的主因是 4.7 吋對於一個不愛帶包包的人而言太大了點 :P

另外,不小心手滑了多買兩個配件(殼x1, 保護套x1) XD

小米2s & 添購配件

看到小米機的包裝,好精緻啊...連開箱都想多拍幾張,總之,這算是近期可以開心的事吧?十一月真是折磨人!

@ 2013/11/28:
看來衰運未過,拿到傳說會"自動關機"的小米2s XDD 馬上去維修報修,順利又換一台,這次終於不會自動關機了。 
自動關機的現象: 
  • 手機螢幕關掉 & 有 wifi 連線時,收到 Line, Skype, Facebook 甚至別人打電話進進來時,手機呼吸燈會亮,但不久後就關機或是準備按 power 鍵顯示時就關機
  • 玩遊戲的過程中會直接關機,開機時顯示電池只剩 1% 等奇異現象。
 解法:換一台即可!

2013年11月7日 星期四

[Python] Bottle + Nginx + uWSGI @ Ubuntu 12.04

環境:

$ sudo apt-get install python-bottle nginx uwsgi uwsgi-plugin-python

Python Bottle 範例 (/var/www/bottle_app):

$ vim /var/www/bottle_app/app.py
from bottle import Bottle, route, run, install, template, static_file
#from bottle_sqlite import SQLitePlugin

#import bottle
#application = bottle.default_app()

app = application = Bottle()
#app.install(SQLitePlugin(dbfile='/tmp/test.db'))

@app.route('/')
def index():
        return template('Hello world')

if __name__ == '__main__':
        run(app, host='0.0.0.0', port=1234)


設定 uWSGI:

$ sudo vim /etc/uwsgi/apps-available/bottle_app.ini
[uwsgi]
socket = /run/uwsgi/app/bottle_app/socket
chdir = /var/www/bottle_app
master = true
plugins = python
file = app.py
uid = www-data
gid = www-data
$ sudo ln -s /etc/uwsgi/apps-available/bottle_app.ini /etc/uwsgi/apps-enabled/bottle_app.ini
$ sudo service uwsgi restart


設定 nginx:

$ sudo vim /etc/nginx/sites-available/bottle_app
upstream _bottle {
server unix:/run/uwsgi/app/bottle_app/socket;
}

server {
listen 8080;
root /var/www/bottle_app;

location / {
try_files $uri @uwsgi;
}

location @uwsgi {
include uwsgi_params;
uwsgi_pass _bottle;
}
}

$ sudo ln -s /etc/nginx/sites-available/bottle_app /etc/nginx/sites-enabled/bottle_app
$ sudo service nginx restart


其他部分,對於 /var/www/bottle_app/app.py 留意 application 變數是 uwsgi 尋找的對象,若想要改變可以在 uwsgi 定義 callable (預設 callable = application)。

至於使用 bottle 開發上麻煩的地方是 templates(views) 更新後,必須重新啟動才能生效,就算改用 uwsgi 也沒有改善,不知是不是少設定甚麼,或是預設規劃就是如此?

除此之外,若不用那麼著重效率,還可以替換掉 Bottle 預設的 wsgiref WSGIServer (non-threading HTTP server),而且改法很簡單(switching-the-server-backend):

$ sudo apt-get install python-paste
$ vim app.py
...
if __name__ == '__main__':
import paste
run(
app ,
host='0.0.0.0',
port=1234,
server='paste'
)

2013年11月2日 星期六

致我們終將失去的...專案

鬥獸場

趕著週五下班享受週末前,在忠孝復興等捷運時,眼前的白髮蒼蒼母親,正與手邊一位貌似唸國小的聊天,不知為何,這讓我想起了「致青春」這部電影的宣傳語,但妙的是,我很直接的用『專案』代替了青春,不曉得是不是「小孩」讓我想到了產出?

熱血的學弟覺得最近的我太悲觀了,或許這正是三十歲的更年期?還是去了一趟義大利被『時間』給震撼住了 XD

聊聊三十歲的同濟在做什麼好了,漸漸看不到追求夢想的身影,看到的是如何計較手中的銅板,大概出社會後衝了幾年後,失敗了幾次後,看重的是每一次真正留在手上的是什麼。面對未來,大家不再講公司的夢想有多大,頂多上進的還能繼續追求卓越的技術。

這是一個很現實的問題,畢竟專案可能會消失,儘管錢會貶值卻仍存在啊!其次,對於股票的價值也不怎看重,並不是股票不在意,而是要談先把現金拉到水準,股票是多餘的那種。如果創業十間只有一間成功,那發的股票是不是要先乘個 10% 算個期望值呢 XD



這陣子讀了不少跟“時間”有關的文章影片,某個角度上,生活就是不斷地把時間換成世俗間最容易評估的價值:貨幣。若不認同公司走法時,要不就拿錢做事,要不換個環境吧!孟母三遷人人皆知,人定勝天在某個條件下不是不可能,但為何要折磨自己呢 XD 或許,更要傻傻地做,不要得太多資訊,以免亂了陣腳 XD

我想,三十歲、同濟、臺北,這三個東西混起後,特別刺鼻。某個角度來說,該想想自己在做什麼,到底想要怎樣的未來了!如果環境不能讓你持續進步,或許再過個三、五年就真的毫無競爭力了(如果人人都能寫程式???)。還有,對岸的環境越來越優渥了!!政府都還會打造免水電租金的辦公室供新創公司進駐(創業、創新 我在台北與成都),想著想著,是不是該放手一下啊?

想清楚自己想要的,不要人云亦云,正是三十歲需要好好思考的一件事。套句數學系同學說的話:「偉大的數學家,都是在四十歲前成名的」,好好珍惜時間吧。

2013年11月1日 星期五

使用 mutt 產生 message/rfc822 測資 @ Ubuntu 12.04

最近正在研究 email 格式,其中一個 content-type: message/rfc822 很神秘,好像很久沒看過了?甚至 2010 年 GMail 相關討論串 中,仍看得到抱怨 GMail 不處理 (現況經測試,GMail 都會將 message/rfc822 展開)

接著就是來產生 message/rfc822 了,這似乎比較常從 outlook 那邊看到,但懶得用outlook 產品,直接用 mutt 吧!

使用 mutt 時,可以設定 mime forward 機制後,接著每一封轉信都可以用 message/rfc822 格式寄出。

$ vim ~/.muttrc
set mime_forward=yes
set mime_forward_rest=yes


或是使用 mutt 時,按 : 去設定上述兩個值亦可。

一封寄給自己的信:

Return-Path: <changyy@vm>
X-Original-To: changyy@vm
Delivered-To: changyy@vm
Received: by vm (Postfix, from userid 1000)
        id 03701604E5; Sat, 26 Oct 2013 10:53:47 +0800 (CST)
Date: Sat, 26 Oct 2013 10:53:47 +0800
From: changyy <changyy@vm>
To: changyy <changyy@vm>
Subject: Test message/rfc822
Message-ID: <20131026025347.GA17760@vm>
MIME-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
User-Agent: Mutt/1.5.21 (2010-09-15)
Status: RO
Content-Length: 9
Lines: 1

as title


把上述那封用 message/rfc822 轉寄給自己:

Return-Path: <changyy@vm>
X-Original-To: changyy@vm
Delivered-To: changyy@vm
Received: by vm (Postfix, from userid 1000)
        id B96A9604E5; Sat, 26 Oct 2013 10:54:13 +0800 (CST)
Date: Sat, 26 Oct 2013 10:54:13 +0800
From: changyy <changyy@vm>
To: changyy <changyy@vm>
Subject: [changyy@vm: Test message/rfc822]
Message-ID: <20131026025413.GB17760@vm>
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="KsGdsel6WgEHnImy"
Content-Disposition: inline
User-Agent: Mutt/1.5.21 (2010-09-15)
Status: RO
Content-Length: 703
Lines: 29


--KsGdsel6WgEHnImy
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

forward test

--KsGdsel6WgEHnImy
Content-Type: message/rfc822
Content-Disposition: inline

Return-Path: <changyy@vm>
X-Original-To: changyy@vm
Delivered-To: changyy@vm
Received: by vm (Postfix, from userid 1000)
        id 03701604E5; Sat, 26 Oct 2013 10:53:47 +0800 (CST)
Date: Sat, 26 Oct 2013 10:53:47 +0800
From: changyy <changyy@vm>
To: changyy <changyy@vm>
Subject: Test message/rfc822
Message-ID: <20131026025347.GA17760@vm>
MIME-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
User-Agent: Mutt/1.5.21 (2010-09-15)

as title

--KsGdsel6WgEHnImy--

2013年10月30日 星期三

[Linux] 監控記憶體使用量,以 python 為例 @ Ubuntu 12.04

監控方式就...去翻 /proc/pid/status 來看

粗略測試方式:
  • 寫一隻程式,最後一行跑 loop 讓程式不會結束,或是讀 io卡住也行
  • 用 ps 得知此程式 pid,接著翻 /proc/pid/status 出來
例如我想知道 python 記憶體使用上會不會 copy by reference 或 copy by reference,那就重複把某個 string data 設值給別人看看,最後用一個 loop 卡住,讓程式不會結束,接著就用 ps aux | grep 'test.py' 去找出來 PID ,再去 cat /proc/PID/status 出來。

test.py:

import time

data = open('tmp').read()
i = 1000
co = []
while i > 0 :
        #co.append(str(data)+"1")
        #co.append(str(data)+"")
        co.append(str(data))
        i -= 1
print "Done:", len(co)
while True:
        time.sleep(1)


連續動作:

$ ps aux | grep test.py | grep -v "grep\|vim" | awk '{system("cat /proc/"$2"/status");}' | grep -i "pid\|vm\|name"

當 co.append(str(data)) 跟 co.append(str(data)+"") 時,i 數值變大對記憶體增加不多:

Name:   python
Pid:    12180
PPid:   11910
TracerPid:      0
VmPeak:    32532 kB
VmSize:    32532 kB
VmLck:         0 kB
VmPin:         0 kB
VmHWM:      5676 kB
VmRSS:      5676 kB
VmData:     2700 kB
VmStk:       136 kB
VmExe:      2496 kB
VmLib:      5240 kB
VmPTE:        72 kB
VmSwap:        0 kB


但 co.append(str(data)+" ") 時,i 數值變大對記憶體就開始噴了:

Name:   python
Pid:    12189
PPid:   11910
TracerPid:      0
VmPeak:   340532 kB
VmSize:   340532 kB
VmLck:         0 kB
VmPin:         0 kB
VmHWM:    313496 kB
VmRSS:    313496 kB
VmData:   310700 kB
VmStk:       136 kB
VmExe:      2496 kB
VmLib:      5240 kB
VmPTE:       680 kB
VmSwap:        0 kB

2013年10月26日 星期六

[Linux] 研究 c-icap modules 與 Squid 相關設定 @ Ubuntu 12.04

前鎮子剛用 pyicap 完成簡單的 ICAP 實作,但 pyicap 還是有一點不順,所以就來試試 c-icap 吧!盡管在 wikipeida 上查閱了幾種 icap server 的實作,但不少已經說不繼續維護,然後就叫大家去看 c-icap 啦,再加上 c-icap 也可以在 apt-get 上方便取得,所以,就來摸摸 so 吧!(據說中國那邊還很盛行,ISP還是會幫忙把網頁加上自家廣告)

先聊一下 c-icap 的部分,它是一隻用 C 實作 ICAP server 的角色,而 Squid 本身就內建 ICAP client 的角色了。使用 c-icap 的理由,則是他提供模組的方式,將你想做的事寫成 module ,設定後可變成 c-icap 的一個 service ,僅需依照 c-icap 的框架實作必要的函式,可省去實作 ICAP 協定。

此篇記錄著重在如何實作 c-icap module ,而網路資料不多,最佳解就是看程式碼 :P

$ apt-get source c-icap libc-icap-mod-urlcheck

此過程會產生兩個目錄:c-icap-0.1.6 和 c-icap-modules-0.1.6,其中 c-icap-modules-0.1.6 環境可以專門拿來編 c-icap modules,但其實最簡單的 c-icap-module 是在 c-icap-0.1.6/services/echo/srv_echo.c,如其名,echo 這只是個範例,但註解非常豐富,看他就夠啦。

其中比較特別的,就是查看 c-icap-module 框架,請參考這段:

CI_DECLARE_MOD_DATA ci_service_module_t service =
{
"echo", /* mod_name, The module name */
"Echo demo service", /* mod_short_descr,  Module short description */
ICAP_RESPMOD | ICAP_REQMOD, /* mod_type, The service type is responce or request modification */
echo_init_service, /* mod_init_service. Service initialization */
NULL, /* post_init_service. Service initialization after c-icap configured. Not used here */                                            
echo_close_service, /* mod_close_service. Called when service shutdowns. */
echo_init_request_data, /* mod_init_request_data */
echo_release_request_data, /* mod_release_request_data */
echo_check_preview_handler, /* mod_check_preview_handler */
echo_end_of_data_handler, /* mod_end_of_data_handler */
echo_io, /* mod_service_io */
NULL,
NULL
};


各項簡介:
  • 此 c-icap module service 啓動與關閉:echo_init_service, echo_close_service
  • 當 request 進來:echo_init_request_data
  • 取得 request preview:echo_check_preview_handler
  • 當 request 處理中:echo_io
  • 當 request 處理完:echo_release_request_data

舉個例來說,如果要用 PCRE 去做處理,一開始要初始化 PCRE 的項目可以擺在 echo_init_service ,而釋放 PCRE 的資源可以擺在 echo_close_service,而每次處理 request 前需要初始化資源則擺在 echo_init_request_data,釋放在 echo_release_request_data。

至於更深的應用就要去 c-icap-modules-0.1.6/service 翻 clamav 和 url_check,這兩項都跟資安相關,前者是掃毒軟體,後者則是可以拿來過濾惡意網站。有興趣可以翻一輪,比較特別的是處理 request 進來的資料,有用到 ci_simple_file_* 和 ci_membuf_* 系列的函式庫,簡言之就是記憶體擺不下就放檔案,或是有些函式只支援掃檔,那就先擺在檔案去進行。這邊便就不多講了。

設定 c-icap:

$ sudo cp output/changyy.so /usr/lib/c_icap/
$ sudo vim /etc/c-icap/c-icap.conf
Service changyy changyy.so
$ sudo /etc/init.d/c-icap restart


設定 squid:

icap_enable on
icap_send_client_ip on
icap_send_client_username on
icap_client_username_encode off
icap_client_username_header X-Authenticated-User
icap_preview_enable on
icap_preview_size 1024

icap_service service_req reqmod_precache bypass=1 icap://localhost:1344/changyy
adaptation_access service_req allow all
icap_service service_resp respmod_precache bypass=1 icap://localhost:1344/changyy
adaptation_access service_resp allow all


其中 icap://localhost:1344/changyy 在此的意義就是 c-icap 中的 Service changyy 啦,最後一提的是 c-icap.conf 有 DebugLevel ,這個數字攸關 debug 訊息,在 c-icap-modules 中許多實作都會用 ci_debug_printf 來印訊息,其中第一個數字若小於等於 DebugLevel 才會被印出來。而輸出的 log 可以在 /var/log/c-icap/server.log 查到。

2013年10月24日 星期四

[OSX] 製作 Mac OS X Mavericks USB 安裝碟 @ Mac 10.9

既然 Mac OS X Mavericks 已經大方到變成免費了!那製作 USB 安裝碟也是必要的技能啊

首先,在先從 App Store 下載 OS X Mavericks 安裝程式,下載完時,先不要去安裝他,可以用以下指令製作 USB 安裝碟,此處假設隨身碟是在 /Volumns/8GB:

$ sudo /Applications/Install\ OS\ X\ Mavericks.app/Contents/Resources/createinstallmedia --volume /Volumes/8GB --applicationpath /Applications/Install\ OS\ X\ Mavericks.app --nointeraction

如此一來就完成啦。

2013年10月23日 星期三

[OSX] 使用 Command line 啓動 VNC 和設定連線密碼 @ Mac OSX 10.8

如果說,都用 Mac OS X 遠端 Mac OS X,那的確不太需要設定 VNC 密碼,因為會轉換到 Mac OS X 自己的協定,但如果是從 Windows/Linux 連到 Mac OS X 的話,設定密碼還是安全一點點。

啓動 VNC:

$ sudo defaults write /var/db/launchd.db/com.apple.launchd/overrides.plist com.apple.screensharing -dict Disabled -bool false
$ sudo launchctl load /System/Library/LaunchDaemons/com.apple.screensharing.plist


設定 VNC 密碼:

$ sudo /System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Resources/kickstart -activate -configure -access -off -restart -agent -privs -all -allowAccessFor -allUsers -clientopts -setvncpw -vncpw VNC_PASSWORD
Starting...
bind(): Address already in use
com.apple.screensharing: Already loaded
Activated Remote Management.
Stopped ARD Agent.
Stopped VNC Privilege Proxy
buffalo: Set user remote access.
macports: Set user remote access.
ookon: Set user remote access.
Set the client options.
Setting allow all users to YES.
Setting all users privileges to ##########.
Done.

[Linux] 使用 virsh 建立 snapshot (管理KVM) @ Ubuntu 12.04

繼上一篇 [Linux] 安裝 Kernel Virtual Machine (KVM) 與純文字操作 後,接著是想著該如何使用 virsh 指令管理 VM,如建立 snapshot 、恢復等。

$ virsh list --all
 Id Name                 State
----------------------------------
  1 vmlinux               running

$ virsh snapshot-list vmlinux
 Name                 Creation Time             State
------------------------------------------------------------


接著嘗試建立 snapshot:

$ sudo virsh snapshot-create vmlinux
error: Requested operation is not valid: Disk '/var/lib/libvirt/images/vmlinux.img' does not support snapshotting

發現目前的格式不支援,接著登入 vmlinux 把機器關掉,進行格式轉換並且更新原本 VM 的敘述檔:

$ sudo qemu-img convert -f raw -O qcow2 /var/lib/libvirt/images/vmmail.img /var/lib/libvirt/images/vmmail.qcow2

$ virsh dumpxml vmlinux
...
  <devices>
    <emulator>/usr/bin/kvm</emulator>
    <disk type='file' device='disk'>
      <driver name='qemu' type='raw'/>
      <source file='/var/lib/libvirt/images/vmlinux.img'/>
...
$ sudo virsh edit vmlinux
...
  <devices>
    <emulator>/usr/bin/kvm</emulator>
    <disk type='file' device='disk'>
      <driver name='qemu' type='qcow2'/>
      <source file='/var/lib/libvirt/images/vmlinux.qcow2'/>
...


重新啟動VM:

$ sudo virsh start vmlinux
$ virsh list --all
 Id Name                 State
----------------------------------
  1 vmlinux               running


建立 snapshot:

$ sudo virsh snapshot-create vmlinux
Domain snapshot 1382549415 created

$ virsh snapshot-list vmlinux
 Name                 Creation Time             State
------------------------------------------------------------
 1382549415           2013-10-23 20:30:15 +0800 running


指定回復:

$ sudo virsh snapshot-revert vmlinux
error: --snapshotname or --current is required

$ sudo virsh snapshot-revert vmlinux --snapshotname 1382549415
$ virsh list --all
 Id Name                 State
----------------------------------
  1 vmlinux               running


上述操作過程中,沒有把 VM 關掉,唯一的差別是連到 VM 的 putty 斷線了而已,也算挺方便的。

2013年10月22日 星期二

[Python] 使用 PyICAP 淺玩 ICAP 與 Squid (以 Response Modification / RESPMOD 為例) @ Ubuntu 12.04

最近在研究 Internet Content Adaptation Protocol (ICAP),這東西好玩之處是可以跟 Proxy server 整再一起,當 proxy 幫 client 去要資料前後,可以透過 ICAP 來將資料加工處理一下。此協定主要可分成兩種資料流:Request Modification 和 Response Modification,直接看圖最清楚:

Request Modification:

Request Modification

Response Modification:

Response Modification

在 Ubuntu 中,可以用 apt-cache search icap 可以看到少少的套件可以把玩(用病毒掃描、限制網址瀏覽等),舉例來說,當使用者要瀏覽一個惡意網站前,可以先阻擋下來;當使用者下載完一個檔案時,可以幫忙掃描,若是病毒則可以擋下。至於我想把玩的主因之一是跟人閒聊一些免費 AP 時,很好奇它是怎樣做到幫網頁加料的,也就是用了他們家的 AP 後,瀏覽的網頁都會多個廣告,以此為目標加以進行下,此為 Response Modification 例子。

首先感謝 pyicap 這小又不簡單的 framework (ICAP 說難不難,說簡單也不簡單,詳請請看 RFC 3507),再次感謝作者佛心分享,依照著內附的 example/respmod_copy.py 小改一下,實作方向此:

  • 告訴 ICAP-client (Squid) 不要對 HTML, HTM 做 preview
  • 挑出 content-type 是 HTML 出來
  • 處理 HTTP 資料,將 gzip 的解開
  • 更新 <body> 資料
  • 將資料在壓成 gzip 後丟出去

片段程式碼(完整版):

#!/bin/env python
# -*- coding: utf8 -*-

import random
import SocketServer

from pyicap import *

import StringIO
import gzip
import re

#...

class ICAPHandler(BaseICAPRequestHandler):

def example_OPTIONS(self):
# ...
self.set_icap_header('Transfer-Complete', 'html,htm')
# ...

def example_RESPMOD(self):
# ...
if self.preview:
# ...
elif analysis_flag:
# ...
try:
orig_data = ''
if content_encoding in ('gzip', 'x-gzip', 'deflate'):
if content_encoding == 'deflate':
data = StringIO.StringIO(zlib.decompress(raw))
else:
data = gzip.GzipFile('', 'rb', 9, StringIO.StringIO(raw))
orig_data = data.read()

#formated_out = orig_data.replace( "<body>", """
formated_out = re.sub(r"(<body[^>]*>)", """
<body>
<!-- src from: http://zh-yue.wikipedia.org/wiki/%E4%B8%96%E7%95%8C%E4%B8%89%E5%A4%A7%E5%A4%9C%E6%99%AF -->
<center><img src="http://upload.wikimedia.org/wikipedia/commons/thumb/4/42/Victoria_Harbour_around_Chinese_New_Year_Night_with_Fireworks_and_Laser_Show.jpg/1024px-Victoria_Harbour_around_Chinese_New_Year_Night_with_Fireworks_and_Laser_Show.jpg" /></center>
""", orig_data )

data = StringIO.StringIO()
f = gzip.GzipFile(fileobj=data, mode='w')
f.write(formated_out)
f.close()

self.write_chunk( data.getvalue() )

need_output = False

except Exception, e:
print e

if need_output:
self.write_chunk(raw)

# ...


Squid 設定(/etc/squid3/squid.conf):

icap_enable on
icap_send_client_ip on
icap_send_client_username on
icap_client_username_encode off
icap_client_username_header X-Authenticated-User
icap_preview_enable on
icap_preview_size 1024
#icap_service service_req reqmod_precache bypass=1 icap://127.0.0.1:13440/squidclamav
#adaptation_access service_req allow all
icap_service service_resp respmod_precache bypass=1 icap://127.0.0.1:13440/example
adaptation_access service_resp allow all


成果,瀏覽一則 Yahoo 新聞時,最上頭被植入一個張圖檔(紅色框框):

Response Modification example

[Python] 處理 HTTP gzip 的資料 @ Ubuntu 12.04

摸了一下 Python 小東西,過程中會去接收 Web server 收下的資料,其中有的會編碼為 gzip ,這時就用 python 稍微解一下:

import StringIO
import gzip

data = gzip.GzipFile('', 'rb', 9, StringIO.StringIO(raw)) # ('filename', 'read/write mode', compression level)
orig_data = data.read()


反過來,把字串 gzip 一下:

import StringIO
import gzip

data = StringIO.StringIO()
f = gzip.GzipFile(fileobj=data, mode='w')
f.write(orig_data)
f.close()

raw = data.getvalue()

2013年10月18日 星期五

[Linux] 安裝 Kernel Virtual Machine (KVM) 與純文字操作 @ Ubuntu 12.04

kvm install  nographics

最近比較常用 VirtualBox 來工作,但如果有一台好一點的機器,每次工作都要用圖形介面也稍顯麻煩,於是就嘗試將那一台好機器裝成 Ubuntu 12.04 64-Bit,接著再裝 kvm 來用,未來就單純透過 terminal 就能完成工作了(KVM也有圖形介面可以用)。

確認 CPU 是否支援虛擬化:

$ sudo apt-get install cpu-checker
$ kvm-ok
INFO: /dev/kvm exists
KVM acceleration can be used


另外,也有人常用以下指令檢查(有吐資料就行):

$ egrep '(vmx|svm)' --color=always /proc/cpuinfo

安裝 kvm:

$ sudo apt-get install kvm

檢查 kvm (此例是用一顆 Intel CPU):

$ lsmod | grep kvm
kvm_intel             137888  0
kvm                   422160  1 kvm_intel

查看一下相關模組:

$ modprobe -l | grep kvm
kernel/arch/x86/kvm/kvm.ko
kernel/arch/x86/kvm/kvm-intel.ko
kernel/arch/x86/kvm/kvm-amd.ko


安裝 kvm 相關常用工具(其中 virt-manager 是圖形化介面,此例可以不安裝):

$ sudo apt-get install libvirt-bin virt-manager virtinst

檢查此台 Host OS 機器狀態:

$ virsh nodeinfo
CPU model:           x86_64
CPU(s):              8
CPU frequency:       1600 MHz
CPU socket(s):       1
Core(s) per socket:  4
Thread(s) per core:  2
NUMA cell(s):        1
Memory size:         8024168 kB


查看網卡設定是否有 virbr 系列:

$ ifconfig | grep virbr
virbr0    Link encap:Ethernet  HWaddr 11:22:33:44:55:66


查看目前虛擬機器狀態:

$ virsh list --all
 Id Name                 State
----------------------------------


建立 VM (Guest OS) 其名為 vmproxy,安裝一台 Ubuntu 12.04 64-bit server 版:

$ sudo virt-install --connect=qemu:///system  --name vmproxy --arch=x86_64 --vcpus=2 --ram=1024 --os-type=linux --hvm --network=bridge:virbr0 --network=network:default --hvm --nographics --accelerate --location http://tw.archive.ubuntu.com/ubuntu/dists/precise/main/installer-amd64/ --disk path=/var/lib/libvirt/images/vmproxy.img,size=20 --extra-args="auto text console=tty1 console=ttyS0,115200"

其中 --location http://tw.archive.ubuntu.com/ubuntu/dists/precise/main/installer-amd64/ 代表要安裝 Ubuntu 12.04 64Bit 系列的,而 --extra-args 的參數是為了解決 KVM 接收 Guest OS console 的問題;--network=bridge:virbr0 --network=network:default 代表配置兩張網卡,一張是 virbr0 ,另一張是預設,而預設網路會走 NAT。

安裝過程須留意的是網路設定,依上述指令,第二張網路卡會是走 NAT 的,可以先採用這張,回頭再去設定 br0 的問題。最後的安裝畫面,記得選一下 Base Ubuntu Server 跟 Openssh server 囉。

kvm install

接著當 Guest OS 重新開機後,可以登入試試,並確認網路狀態(連外是否ok),另外有興趣也可以印一下 cpuinfo 囉。

vmlinux:~$ ifconfig
eth1      Link encap:Ethernet  HWaddr 52:54:00:a7:b7:c5
          inet addr:192.168.122.250  Bcast:192.168.122.255  Mask:255.255.255.0
...

vmlinux:~$ cat /proc/cpuinfo
processor       : 0
vendor_id       : GenuineIntel
cpu family      : 6
model           : 2
model name      : QEMU Virtual CPU version 1.0
stepping        : 3
microcode       : 0x1
cpu MHz         : 3392.292
cache size      : 4096 KB
fpu             : yes
fpu_exception   : yes
cpuid level     : 4
wp              : yes
flags           : fpu de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pse36 clflush mmx fxsr sse sse2 syscall nx lm up rep_good nopl pni cx16 popcnt hypervisor lahf_lm
bogomips        : 6784.58
clflush size    : 64
cache_alignment : 64
address sizes   : 40 bits physical, 48 bits virtual
power management:

接著回到原先的 Host OS 可以用 virsh list 查看:

$ virsh list --all
 Id Name                 State
----------------------------------
  1 vmproxy              running


如果要強制停掉:

$ virsh destroy vmproxy

若要刪掉它的資訊:

$ virsh undefine vmproxy

做完 undefine 後,下次 virsh-install 就可以再使用此名字,但若要完整刪掉,還須清除 disk 位置(此例為 /var/lib/libvirt/images/vmproxy.img),也可用圖形界面一次處理好。

若透過 Host OS 的 port forwarding 讓外頭可以直接連入 Guest OS,則可以用用 redir 這個指令:

$ sudo apt-get install redir
$ /usr/bin/redir --lport=20022 --caddr=192.168.122.250 --cport=22 &


最後,雖然 Host OS 預設不會關機,但還是設定一下自動將 Guest OS 啟動的方式:

$ sudo vim /etc/init.d/vm-init.sh
#!/bin/sh
virsh start vmproxy
/usr/bin/redir --lport=20022 --caddr=192.168.122.250 --cport=22 &

$ sudo chmod 755 /etc/init.d/vm-init.sh
$ sudo update-rc.d -f vm-init.sh defaults

[Linux] 修正 wget 下載檔案名字 @ Ubuntu 12.04

常用 wget 下載檔案,但碰到一些會做重導的網頁或是檔名是在 HTTP HEAD 時,用瀏覽器下載可以正確取得檔名,但用 wget 只會看網址。修正方式只是叫 wget 去相信 server 回傳的檔名而已。某個角度來說,wget 預設不這樣做或許有安全性的考量?

例如下載 Ubuntu 64-bit Server LTS ISO 檔:

$ wget 'http://www.ubuntu.com/start-download?distro=server&bits=64&release=lts'
...
HTTP request sent, awaiting response... 200 OK
Length: 697303040 (665M) [application/octet-stream]
Saving to: `start-download?distro=server&bits=64&release=lts'
...


加上 --trust-server-names:

$ wget --trust-server-names 'http://www.ubuntu.com/start-download?distro=server&bits=64&release=lts'
...
HTTP request sent, awaiting response... 200 OK
Length: 697303040 (665M) [application/octet-stream]
Saving to: `ubuntu-12.04.3-server-amd64.iso'
...

2013年10月15日 星期二

[Python] urlib2 設定 User-Agent (偶爾可解掉 urllib2.HTTPError: HTTP Error 403: Forbidden )

雖然造成 403: Forbidden 的因素有很多,但有一種是網站不想讓 crawler 抓資料,但 User-agent 設定完就能解掉,所以此例拿來記錄 urllib2 設定 User-Agent 的過程(盡量先不請 pycurl 出來 XD)。

原本用法:

obj = urllib2.urlopen('http://www.google.com/')

加入 User-Agent 用法:

obj = urllib2.urlopen( urllib2.Request( 'http://www.google.com/' , None , { 'User-Agent' : 'Mozilla/5.0' } ) )

另外紀錄一下 wget 用法:

$ wget -U 'Mozilla/5.0' http://www.google.com

2013年10月11日 星期五

[Linux] 使用 redir 做 port forwarding 與設定開機啓動 @ Ubuntu 12.04

最近又在一台擁有 public ip 的強悍機器上架 VM 了,暫時是用 Ubuntu 12.04 desktop 跑 VirtualBox 來用,所以就碰到如何從外頭連進去的需求,就裝裝 redir 來用吧!
$ sudo apt-get install redir
舉例來說,將 Host OS 的 20022 port 導向到 Guest OS 的 22 port,讓人可以從外頭來進去:
$ /usr/bin/redir --lport=20022 --caddr=192.168.56.101 --cport=22
設成開機啓動:
$ sudo vim /etc/init.d/vbox-port-forwarding.sh
#!/bin/sh

# port forwarding
/usr/bin/redir --lport=20022 --caddr=192.168.56.101 --cport=22 &

$ sudo chmod 755 /etc/init.d/vbox-port-forwarding.sh
$ sudo update-rc.d -f vbox-port-forwarding.sh defaults

2013年10月8日 星期二

安裝 Eclipse 與 Eclipse CDT 支援 C/C++ Project 開發與偵錯 @ Ubuntu 12.04 Desktop

前輩詢問在 Ubuntu 上頭有沒合適的 GUI Debug 方式,我想了很久,實在是我連 gdb 都不常用,學生時代土法煉鋼的 printf 就夠我解決九成問題,此不便的 debug 方式,會讓人謹記教訓,也有訓練記憶之功用? 只能說我接觸的 Project 還不夠大吧 :P 我有印象第一次真的去用 gdb 是因為我寫了類似很多層 struct 的情況。

開始還在想要不要請前輩用 gdb -tui ,所幸還有想到 Eclipse 啦。廢話不多說,來個圖文教學吧。

安裝 Eclipse CDT:

由於 Ubuntu 12.04 裝完的 Eclipse 是 INDIGO 3.7.2 版本,所以在 Eclipse -> Help -> Install New Sotfware 那邊,把 Work with 填入 http://download.eclipse.org/tools/cdt/releases/indigo  (更多選擇請參考 http://www.eclipse.org/cdt/downloads.php)

Ubuntu 12.04 Eclipse install CDT 1

接著偷懶全選,然後就會出現問題 Orz 接著去掉重複項目的舊版、去掉仍有問題的項目,最後就可以安裝了。

Ubuntu 12.04 Eclipse install CDT 2

理當來個 Hello World 範例:

Ubuntu 12.04 Eclipse New C++ Project

Ubuntu 12.04 Eclipse New C++ Project 2

Ubuntu 12.04 Eclipse New C++ Project 3

Ubuntu 12.04 Eclipse New C++ Project 4

Ubuntu 12.04 Eclipse New C++ Project 5

Ubuntu 12.04 Eclipse New C++ Project 6

Ubuntu 12.04 Eclipse New C++ Project 7

Ubuntu 12.04 Eclipse New C++ Project 8

Ubuntu 12.04 Eclipse New C++ Project 9

Ubuntu 12.04 Eclipse New C++ Project 10

再補一個 Debug 流程:

Ubuntu 12.04 Eclipse C++ Project Debug 1

Ubuntu 12.04 Eclipse C++ Project Debug 2

Ubuntu 12.04 Eclipse C++ Project Debug 3

Ubuntu 12.04 Eclipse C++ Project Debug 4

Ubuntu 12.04 Eclipse C++ Project Debug 5

Ubuntu 12.04 Eclipse C++ Project Debug 6

Ubuntu 12.04 Eclipse C++ Project Debug 7

2013年10月5日 星期六

Mac mini (Late 2009) 更換硬碟

macmini2009late_hd_upgrade_01

幾個月前,某台 Windows 筆電開機一直會找不到硬碟,那時擔心硬碟快壞了,所以買了一顆新硬碟來用,卻發現沒有解決現象,只能猜測單純房間溫度過高吧 XD 於是,多了一顆 500GB 硬碟,就替 2010 年買的 Mac mini 替換一下,拆機後才發現這台才 160GB 而耶,真股奇妙的感覺:Mac mini(2009 年末)- 技術規格

翻翻三年前替它更換記憶體的文章(Mac mini 拆殼 + 更換記憶體),不再像以前那般小心翼翼,單純用一隻鐵尺從後面面板那拆機,不用幾分鐘就拆好外殼了 XD

macmini2009late_hd_upgrade_02

我記得以前查看資料時,發現換硬碟會比較麻煩,但仔細看一下機構,卻也不見得要拆很多東西,算起來是偷吃步的拆法,比換記憶體多拆了 4 棵螺絲而已 :P 拆完鎖硬碟的螺絲後,再透過摩擦力貼著硬碟輕輕往後拉,就可以拆下硬碟,隨機構的設計退出來,再更換硬碟後,傾斜機構讓硬碟插槽可以對好,就可以輕推裝好了。

macmini2009late_hd_upgrade_03

macmini2009late_hd_upgrade_04

Mac mini 強制退出光碟片:開機時,按住滑鼠左鍵

在處理 Mac mini 2009 late 機器時,先擺入了一張舊的 Mac OSX Install 光碟,想說處理後更換新版 OSX 來安裝系統,卻想不到該怎樣退片 XDD

問問 Google 的結果:
開機時,按著滑鼠左鍵,這樣就會退片了 XD
真是神秘的招數。

2013年10月4日 星期五

iOS Developer Program 續約流程

既然去年有寫購買教學(申請 iOS Developer Program 之 Company 版),那就順便記一下續約的好了 XD 簡單的說,離過期一個月前,時會收到 Apple 提醒信件,接著就隨著 Email 給的網址去購買吧!(這種從 Email 點選購買流程要小心病毒信啊 XD)


iOS Developer Promgram 01

iOS Developer Promgram 02

iOS Developer Promgram 03

iOS Developer Promgram 04

iOS Developer Promgram 05

iOS Developer Promgram 06

iOS Developer Promgram 07

iOS Developer Promgram 08

接著,應該是24小時內又會收到啓用通知信,就在網頁上填寫啓用碼即可啓用。

iOS Developer Promgram 09

iOS Developer Promgram 10