우분투 커널 소스 컴파일

리눅스/OS 일반|2017. 10. 23. 10:39
반응형

안녕하세요.

이 글에서는 리눅스(우분투) 커널 컴파일 하는 방법에 대해서 알아보도록 하겠습니다.

 

커널 컴파일 전에 커널에 대해서 알아보도록 하겠습니다.

커널이란, 운영체제에서 시스템의 제어 등을 하는 프로그램을 말합니다.

컴퓨터를 부팅 하면 자동으로 실행되어 메모리에 올라가게 됩니다.

 

이 커널은 수시로 업그레드, 업데이트가 됩니다.

새로운 커널의 소스는 http://kernel.org 에서 다운로드 받으실 수 있구요.

이 사이트에서 받은 소스를 내 컴퓨터에 설치할 수 있게 만드는 과정을 커널 컴파일 이라고 합니다.

 

커널은 여러 사용자(컴퓨터)와의 원활한 호환을 위해 다양한 정보를 담고 있습니다.

때문에 필요한 정보보다는 불필요한 정보가 더 많이 담겨있게 되죠.

능숙한 사용자, 개발자일수록 본인에게 필요한 것만 설정하여 더 최적화 된 커널을 빌드할 수 있게 됩니다.

저 역시 능숙한 개발자가 아니기 때문에…

이 글에서는 커널 판 올림을 위한 커널 컴파일만을 다루도록 하겠습니다.

 

현재 커널 버전 확인

현재 커널 버전을 확인은 다음 명령어를 통해 할 수 있습니다.


$ uname -r

 

이렇게 하면 현재 커널의 버전을 확인할 수 있습니다.


커널 컴파일, 설치 전 제 컴퓨터의 커널 버전은 3.8.0-19-generic 이네요.

 

커널 소스 다운로드

커널 소스는 http://kernel.org 에서 받으실 수 있습니다.

제가 이 글을 쓰는 시점에서의 최신 커널 버전은 3.9.2 네요.

원하시는 버전의 커널 소스를 다운받으시면 됩니다.


 

필요 패키지 설치

커널 컴파일을 하는데 필요한 패키지가 있습니다.

총 5가지 인데요.

다음의 명령어를 통해 설치하시면 됩니다.


$ sudo apt-get install build-essential libncurses5 libncurses5-dev bin86 kernel-package -y

 

위 명령어를 통해 직접 설치 하셔도 되구요.

제가 만든 쉘 스크립트를 다운받으셔서 다음의 명령어를 입력하시면 자동 설치가 됩니다.


 kernel_compile_package.sh



$ sudo chmod 777 kernel_compile_package.sh

$ ./ kernel_compile_package.sh

 

다운로드 받은 커널 소스 이동

다운로드 받은 커널 소스를 '/usr/src/' 디렉토리로 이동시켜 줍니다.

그리고 해당 디렉토리로 이동합니다.


$ sudo mv 커널소스파일명 /usr/src/

$ cd /usr/src


이동하셔서 ls 명령어를 통해 파일이 이동된 것을 확인할 수 있습니다.


 

압축 풀기

커널 소스는 보통 압축된 형태로 배포가 됩니다.

압축을 풀어야 합니다.

제가 받은 압축 파일의 형식은 '.tar.xz' 형식이기 때문에 xz와 tar를 통해 압축을 해제하셨습니다.

(다운받은 형태에 따라 압축 해제 방식은 바뀔 수 있습니다.)

제가 압축을 해제할 때 사용한 명령어는 다음과 같습니다.


$ sudo xz –d linux-3.9.2.tar.xz

$ sudo tar xf linux-3.9.2.tar

 

압축이 정상적으로 해제되었다면 다운로드 받은 커널 소스가 담긴 새로운 디렉토리가 생성된 것을 확인할 수 있습니다.

새로운 디렉토리가 생성되었다면, 해당 디렉토리로 이동합니다.


 

-14.10.01 추가-

Configuration 파일 복사

현재 커널의 configuration 파일을 복사 합니다.

현재 커널 설정을 그대로 가져갈 경우 별다른 수정 없이 사용할 수 있습니다.

먼저 현재 커널의 버전을 'uname -a' 명령어로 확인합니다. (본 포스팅에서의 커널 버전은 3.8.0-19-generic 입니다.)

그리고 현재 커널의 configuration 파일을 복사합니다.

파일 경로 및 파일 명은

/boot/config-현재커널명

입니다.

커널 소스 디렉토리에 '.config' 파일로 복사해 줍니다.

# sudo cp /boot/config-현재커널명 ./.config

본 포스팅 같은 경우엔 3.8.0-19-generic 커널을 사용하고 있으므로

# sudo cp /boot/config-3.8.0-19-generic ./.config

명령어를 사용하였습니다.

(원본 파일명을 치실 때 'tab' 키를 활용하시면 좀 더 편하게 사용하실 수 있습니다.


make menuconfig

커널 소스 디렉토리로 이동한 후엔 현재 내 컴퓨터에 맞는 설정을 해주어야 합니다.

만약 이전에 커널 컴파일 한 기록이 있으면 make mrproper 명령어를 통해 의존성을 삭제합니다. (첫 커널 컴파일이라면 하지 않으셔도 됩니다.


$ sudo make mrproper (첫 커널 컴파일이라면 하지 않아도 됨)

$ sudo make menuconfig

 


제대로 작동 된다면 다음의 화면을 만날 수 있습니다.



이제 이 화면에서 본인에게 필요한 설정을 하시면 됩니다.

하지만 이 화면에서 마음대로 설정 가능하신 분은 이 글을 보지 않으실 테니…

현재 컴퓨터의 설정을 가져오도록 하겠습니다.

 

밑에 Load를 누릅니다.



새로운 터미널 창을 띄워 '/boot' 디렉토리로 이동한 후, '/boot' 디렉토리의 파일을 확인합니다.

그 중 'config-xxx' 라는 파일을 확인합니다. (xxx 는 현재 커널 버전을 말합니다.)


$ cd /boot

$ ls

 



저 파일을 Load 해 줍니다. (절대 경로로 작성해야 합니다.)

/boot/확인한 파일 명

 

 

정상적으로 로드 되었으면 아무런 에러 메시지 없이 처음의 화면을 만나게 됩니다.

이제 추가 수정 없이 저장해주시면 됩니다.

저장은 Save를 눌러 하시면 되고, 저장하는 파일 명은 '.config' 입니다.

정상 저장 되었다면 저장 되었다는 메시지를 확인하실 수 있습니다.


 

정상적으로 저장되었다면


원하시는 설정을 사용하시고, Exit를 눌러 빠져 나오시면 됩니다.


 

커널 컴파일 (커널 설치 이미지 생성)

이제 커널 컴파일을 하여 설치 이미지를 생성하면 됩니다.

다음의 명령어를 통해 커널 컴파일을 실행할 수 있습니다.


$ sudo make-kpkg --initrd --revision=1.0 kernel_image

 

추 후에 같은 버전의 커널을 여러 번 빌드 하게 될 경우 '--revision=' 뒤에 숫자를 올려주시면 됩니다.

'--revision=1.0' '--revision=2.0' 이런 식으로요. 단, 숫자만 입력하셔야 합니다.


 

커널 컴파일을 하는 데는 많은 시간이 소요됩니다.

컴퓨터 성능에 따라 적게는 수 십분, 많게는 3~4시간 까지 걸리기도 합니다.

커널 컴파일 중에는 다음과 같은 복잡한 화면을 보실 수 있습니다.


 

커널 컴파일이 완료되면 다음과 같이 종료가 되구요.


 

커널 컴파일이 정상적으로 완료되었다면, 상위 디렉토리 ('/usr/src')에 커널 이미지가 생성되게 됩니다.

'cd ..' 명령어를 통해 상위 디렉토리로 이동하여 확인해 줍니다.


 

'.dev' 파일이 커널 설치 파일입니다.

이제 이 파일을 사용하여 커널을 설치해주면 됩니다.

 

커널 설치

커널 이미지가 있는 디렉토리(/usr/src)에서 다음 명령어를 입력해 주시면 됩니다.


$ sudo dpkg -i 커널이미지파일명

 

 

설치가 완료된 후엔 컴퓨터를 재부팅 해주시면 됩니다.


 

바뀐 커널 버전 확인

재부팅 후 처음에 커널 버전을 확인할 때 사용하였던 'uname -r' 명령어를 사용하시면 바뀐 커널 버전을 확인하실 수 있습니다.



기존의 3.8.0-19-generic 버전에서 3.9.2 버전으로 바뀐 것을 확인할 수 있습니다.


* 2016년 2월 19일 추가 내용

선량한지구인님 께서 댓글로 좋은 정보를 주셨습니다.

컴파일 도중 "openssl/opensslv.h 그런 파일이나 디렉터리가 없습니다."는 오류가 발생할 경우 libssl 설치를 통하여 해결이 가능하다고 알려주셨습니다. 

다음의 명령어를 통해 설치 가능합니다.

$ sudo apt-get install libssl-dev

좋은 정보 공유해주신 '선량한지구인'님 감사드립니다.

 

 

시스템 프로그래밍 수업이나 운영체제 수업 실습 시간이나 과제로 꼭 한번씩은 해보는 커널 컴파일을 해봤습니다.

궁금한 점이나 되지 않는 부분이 있으면 댓글 달아주시면 아는 한도 내에서 최대한 답변해드리도록 하겠습니다.



출처: http://harryp.tistory.com/9 [Park's Life]


반응형

댓글()

mysqldump 명령시 부하 줄이는 옵션

리눅스/MySQL|2017. 10. 19. 14:50
반응형

--master-data=2 --single-transaction --quick


위 옵션을 추가하여 dump 받으면 아파치 다운등의 문제가 발생하지 않습니다.


반응형

댓글()

원하는 만큼 vm 을 한번에 생성하기 (nova boot --max-count)

리눅스/OpenStack|2017. 10. 19. 14:07
반응형

--max-count 만큼 동시에 인스턴스 생성


example)

# nova boot --flavor SYSDOCU.SINGLE --image centos6 --nic net-id=282f0dcf-7054-40aa-9118-db1aea9db9d0 lkh1 --max-count=3

반응형

댓글()

안드로이드 음성 인식 기능 (STT)

프로그래밍/Android (Java)|2017. 10. 10. 17:37
반응형

In this tutorial we are going to show how to use Android’s Speech To Text API. Android currently supports offline mode also. The only thing is that you have to downlad offline language packages.

To learn how to enable offline mode speech to text, please follow the tutorial How to enable offline Speech To Text in Android

See following Steps :

  1. Start RecognizerIntent intent with the RecognizerIntent.ACTION_RECOGNIZE_SPEECH as action.
  2. Handle the text data returned by Speech to Text API

1. Manifest file

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
    package="com.stackandroid.speechtotext"
    android:versionCode="1"
    android:versionName="1.0" >
    <uses-sdk
        android:minSdkVersion="19"
        android:targetSdkVersion="21" />
    <uses-permission android:name="android.permission.INTERNET"/>
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

2. Activity layout file

activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/bg_color"
    android:orientation="vertical" >
    <TextView
        android:id="@+id/txt_output"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="100dp"
        android:textColor="@color/white"
        android:textSize="26dp"
        android:textStyle="normal" />
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="60dp"
        android:gravity="center"
        android:orientation="vertical" >
        <ImageButton
            android:id="@+id/btn_mic"
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:background="@null"
            android:scaleType="centerCrop"
            android:src="@drawable/microphone" />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:text="Speech to text using Google API"
            android:textColor="@color/white"
            android:textSize="15dp"
            android:textStyle="normal" />
    </LinearLayout>
</RelativeLayout>

3. Main Activity code

Start the speech recognizer intent using startActivityForResult() with bundle extras.

Required extras:

Optional extras:

Result extras (returned in the result, not to be specified in the request):

Note: The extra EXTRA_LANGUAGE_MODEL is mandatory. Also handle ActivityNotFoundException there may be no applications installed in the device to support this speech recognition action.

MainActivity.java
package com.stackandroid.speechtotext;
import java.util.ArrayList;
import java.util.Locale;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.os.Bundle;
import android.speech.RecognizerIntent;
import android.view.View;
import android.widget.ImageButton;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends Activity {
    private final int SPEECH_RECOGNITION_CODE = 1;
    private TextView txtOutput;
    private ImageButton btnMicrophone;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        txtOutput = (TextView) findViewById(R.id.txt_output);
        btnMicrophone = (ImageButton) findViewById(R.id.btn_mic);
        btnMicrophone.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startSpeechToText();
            }
        });
    }
    /**
     * Start speech to text intent. This opens up Google Speech Recognition API dialog box to listen the speech input.
     * */
     private void startSpeechToText() {
        Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
        intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.getDefault());
        intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
                RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
        intent.putExtra(RecognizerIntent.EXTRA_PROMPT,
                "Speak something...");
        try {
            startActivityForResult(intent, SPEECH_RECOGNITION_CODE);
        } catch (ActivityNotFoundException a) {
            Toast.makeText(getApplicationContext(),
                    "Sorry! Speech recognition is not supported in this device.",
                    Toast.LENGTH_SHORT).show();
        }
     }
     /**
      * Callback for speech recognition activity
      * */
     @Override
     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
         super.onActivityResult(requestCode, resultCode, data);
         switch (requestCode) {
         case SPEECH_RECOGNITION_CODE: {
             if (resultCode == RESULT_OK && null != data) {
                 ArrayList<String> result = data
                         .getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
                 String text = result.get(0);
                 txtOutput.setText(text);
             }
             break;
         }
         }
     }
}

4. Demonstration
Speech to text demonstration using RecognizerIntent .

Speech to text demo
Speech to text demo

Offline mode is not enabled

Offline mode not enabled
Offline mode not enabled




[출처] http://stackandroid.com/tutorial/android-speech-to-text-tutorial/

반응형

댓글()

iptables 룰 추가 운선순위값 입력하여 적용시키기

리눅스/Network|2017. 9. 28. 12:58
반응형

예시) 특정 IP 를 차단하려고 함

 

상황) iptables -nL 로 확인시 INPUT 룰이 15개가 있을때

맨 마지막에 추가되면 특정 포트를 오픈시킨 뒤라서 차단되지 않는다. 이를 해결하고자 한다.

 

방법) INPUT 룰에서 특정 포트 오픈이 12~15번째 줄일때 12라인 이전에 추가하면 적용이 된다.

 

[root@sysdocu ~]# iptables -I INPUT 10 -p tcp -s 192.168.10.2 -j DROP    // 10번째 라인에 등록

[root@sysdocu ~]# iptables -D INPUT -p tcp -s 192.168.10.2 -j DROP        // 제거

 

반응형

'리눅스 > Network' 카테고리의 다른 글

Ubuntu 16.04 에서 STUN/TURN 서버 설치  (0) 2018.10.04
네트워크 속도 체크하기 (iperf)  (0) 2018.01.09
네트워크 장치명 변경 (Ubuntu 16.04)  (0) 2017.08.08
Ubuntu 16.04 IPTABLES 사용법  (0) 2017.02.14
DHCP 설치  (0) 2016.07.22

댓글()

웹서버를 통한 파일 자동 업데이트

프로그래밍/Android (Java)|2017. 9. 27. 09:11
반응형

아래 내용은 안드로이드 6.x 까지만 적용되는 예제입니다.

안드로이드 7.x 부터는 다른 예제를 찾아보세요! ㅜ.ㅜ

참고 : http://duongame.tistory.com/263 (Android 7.0 APK 파일 설치)

 

 

 

웹서버 설정

 

1. 파일명 : application.properties

 

#messages for udpate
versionCode=9
versionName=1.1
fileName=TraMainActivity.apk
message=\ufffd\ufffd\u022d\ufffd\ufffd\ufffd\ufffd\u05f9\u6e6e\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd \ufffd\ufffd\ufffd\ufffd \ufffd\u05b5\ufffd\ufffd\ufffd \ufffd\ufffd\ufffd\ufffd\ufffd\u03ff\ufffd\ufffd\ufffd\ufffd\u03f4\ufffd. \ufffd\ufffd\u022d\ufffd\ufffd \ufffd\ufffd\ufffd\ufffd\ufffd\ufffd \ufffd\ufffd\ufffd\ufffd\ufffd\u05b5\ufffd\ufffd\ufffd \ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u0534\u03f4\ufffd.
title=\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u01ae\ufffd\ufffd \ufffd\ufffd\ufffd\ufffd\ufffd\u0574\u03f4\ufffd.

 

message 에서 줄바꿈 하려면 한줄로 적되, 개행할 곳에 \r\n 를 사용하면 된다.

 

2. 원문파일명 : application.properties.source.txt

 

#messages for udpate
versionCode=2
versionName=1.12
fileName=TraMainActivity.apk
message=통화견적및방문견적을 볼수 있도록 수정하였습니다. 정화조 도면을 볼수있도록 수정중입니다.
title=업데이트를 시작합니다. 

 

기존에 설치된 app 의 AndroidManifest.xml 과 웹서버에 있는 application.properties 의 versionCode 를 비교해서

웹서버 versionCode 값이 더 클 경우 앱에서 팝업으로 알려주게 된다. (최대값이 

2147483647

 이다.)

versionName 은 필요시 사용자에게 보여주기 위한 번호일 뿐이다. (비교 안함)

 

AndroidManifest.xml 파일의 옵션값이 먹히지 않을 경우 (AndroidStudio를 사용할 경우) build.gradle (Module: app) 내의 옵션이 우선되기 때문이다.

 

3. 파일 형식 변환

 

원문 파일을 jdk 폴더 내의 bin폴더에 있는 native2ascii.exe 명령으로 바꾼것이다.

 

native2ascii.exe application.properties.source.txt application.properties

 

[사용 방법]

-- versionCode 와 앱의 manifest 파일의 versionCode와 일치시키면 된다. versionName도 마찬가지로 일치시키면 된다.

-- fileName은 앱의 파일 이름을 적어주면된다.

-- message에는 업데이트 된 내용을 적어주면 된다.

-- title은 다운로드 받는 progress dialog의 제목이다.

 

설정 파일 작성을 완료 했다면 설정 파일과 앱파일을 서버상에 같은 폴더에 업로드 한다.

아래 소스에서는 웹서버의 /download/ 디렉토리에 업로드 하도록 하였다.

 

 

 

필수 권한

 

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<uses-permission android:name="android.permission.INTERNET" />

 

 

 

AutoupdateActivity.java

package com.neulwon.study;


import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;




public class AutoupdateActivity extends Activity {


    //새버전의 프로그램이 존재하는지 여부
    private int newver=0;
    private int oldver=0;
    private String strVer;
    
    private String fileName;
    private CharSequence updateMessage;
    private String updateTitle; 
    
    // Progress Dialog 
    private TextView textVersion;
    
    private TextView progressTitle;
    private TextView progressText;
    private ProgressBar progressBar;
    private RelativeLayout downloadUpdateLayout;
    
    //확인하고 싶은 패키지명 String
    private static final String CHECK_PACKAGE_NAME="com.neulwon.study";


    public static final String MSG_TAG = "AutoupdateActivity";
    
    private static final String IP_ADDRESS = "sysdocu.tistory.com";


    // Update Url
    private static final String APPLICATION_PROPERTIES_URL = "http://" + IP_ADDRESS + "/download/application.properties";
    private static final String APPLICATION_DOWNLOAD_URL = "http://" + IP_ADDRESS + "/download/";


    public static final int MESSAGE_DOWNLOAD_STARTING = 3;
    public static final int MESSAGE_DOWNLOAD_PROGRESS = 4;
    public static final int MESSAGE_DOWNLOAD_COMPLETE = 5; 
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_autoupdate);
     
        progressBar = (ProgressBar) findViewById(R.id.progressBar);
        progressText = (TextView) findViewById(R.id.progressText);
        progressTitle = (TextView) findViewById(R.id.progressTitle);
        downloadUpdateLayout = (RelativeLayout) findViewById(R.id.layoutDownloadUpdate);
        
        textVersion = (TextView) findViewById(R.id.textVersion );
        
// 앱실행시 기존 다운로드 파일 삭제
String del_localpath = getDownloadDirectory();
File del_file = new File(del_localpath + "/update-debug.apk");
        del_file.delete();


        // 업데이트 체크
        checkForUpdate();
        
        PackageInfo oldversionInfo;
        try {
            oldversionInfo = getPackageManager().getPackageInfo(CHECK_PACKAGE_NAME,PackageManager.GET_META_DATA);       
            strVer = oldversionInfo.versionName;
        } catch (NameNotFoundException e1) {
            e1.printStackTrace();
        }        
           
        //textVersion.setText( "버전 " + strVer );
textVersion.setText( "IPTV 업데이트" );
    }
   
    //앱 업데이트 검사
    public void checkForUpdate() {           
      new Thread(new Runnable(){
          public void run(){
              Looper.prepare();   
              
              // 서버상의 Properties 얻기
              Properties updateProperties = queryForProperty(APPLICATION_PROPERTIES_URL);
              String verName;
              
              if ( updateProperties != null && updateProperties.containsKey("versionCode") ) {
                
                  int newversion = Integer.parseInt(updateProperties.getProperty("versionCode"));
                  //int installedVersion = TetherApplication.this.getVersionNumber();
                  fileName = updateProperties.getProperty("fileName", "");
                  updateMessage = updateProperties.getProperty("message", "");
                  updateTitle = updateProperties.getProperty("title", "업데이트가 가능합니다.");
                  verName = updateProperties.getProperty("versionName", "");
                  
                  newver = newversion;
                  
                  try {
                      //설치된 앱 정보 얻기
                      PackageInfo oldversionInfo = getPackageManager().getPackageInfo(CHECK_PACKAGE_NAME,PackageManager.GET_META_DATA);
                      oldver = Integer.valueOf(oldversionInfo.versionCode);
                      
                      //다운로드 폴더 얻어오기
                      String localpath = getDownloadDirectory();


                      Log.d("앱버전","앱버전 : " + oldver );
                      Log.d("서버상","서버에 있는 파일 버전 : " + newver );
                      Log.d("받아온경로","받아온 경로: " + localpath);


                      if ( oldver < newver ) {    //파일 버전비교                            
                          openUpdateDialog( APPLICATION_DOWNLOAD_URL + fileName , fileName , updateMessage , updateTitle,localpath);                
                      } else {
                          Log.d(MSG_TAG, " 최신버전입니다. 버전 : " + verName );
                      }
                      //textVersion.setText(" Ver : " + verName);
                  } catch ( Exception e ) {
                      e.printStackTrace();
                  }
              }
              Looper.loop();
          }
      }).start();
    }
   
    //다운로드 폴더 얻기
    private String getDownloadDirectory(){
        String sdcardPath="";
        String downloadpath = "";
        if ( isUsableSDCard(true)){    //외장메모리 사용가능할 경우
            sdcardPath = Environment.getExternalStorageDirectory().getPath();
            //downloadpath = sdcardPath + "/download/";
downloadpath = sdcardPath + "/data/local/tmp/";
        } else {                       //내장메모리 위치
            File file = Environment.getRootDirectory();
            sdcardPath = file.getAbsolutePath();
            //downloadpath = sdcardPath + "/download";
downloadpath = sdcardPath + "/data/local/tmp";
        }
    return downloadpath;
    }
    
    //외장메모리 사용 가능여부 확인   
    private boolean isUsableSDCard(boolean requireWriteAccess) {
        String state = Environment.getExternalStorageState();
        if ( Environment.MEDIA_MOUNTED.equals(state)) {
            return true;
        } else if( !requireWriteAccess &&
               Environment.MEDIA_MOUNTED_READ_ONLY.equals(state) ) {
           return true;
        }
        return false;
    }    
    
    
    //서버의 application.properties 파일 읽어오기
    public Properties queryForProperty(String url) {
        Properties properties = null;
        
        HttpClient client  = new DefaultHttpClient();
        HttpGet request = new HttpGet(String.format(url));
       
        try {
            HttpResponse response = client.execute(request);            
            StatusLine status = response.getStatusLine();
            
            if ( status.getStatusCode() == 200 ) {
                HttpEntity entity = response.getEntity();
                properties = new Properties();
                properties.load(entity.getContent());
            }
        } catch ( IOException e ) {
            Log.d("오류","Can't get property '" + url + "'.");
        }
        return properties;
    }
    
    //업데이트 할 것인지 확인
    public void openUpdateDialog(final String downloadFileUrl, final String fileName,
          final CharSequence message, final String updateTitle,final String localpath4down) {
      
        LayoutInflater li = LayoutInflater.from(this);
        Builder dialog;
        View view;
      
        view = li.inflate(R.layout.updateview, null);
        TextView messageView = (TextView) view.findViewById(R.id.updateMessage);
        TextView updateNowText = (TextView) view.findViewById(R.id.updateNowText);
      
        if (fileName.length() == 0)  // No filename, hide 'download now?' string              
            updateNowText.setVisibility(View.GONE);
       
        messageView.setText(message);                 
      
        dialog = new AlertDialog.Builder(AutoupdateActivity.this)
                               .setTitle(updateTitle)
                               .setView(view);
           
        if (fileName.length() > 0) {
            //dialog.setNeutralButton("취소", new DialogInterface.OnClickListener() {
            //    public void onClick(DialogInterface dialog, int whichButton) {
            //        Log.d(MSG_TAG, "No pressed");
            //    }
            //});
            dialog.setNegativeButton("확인", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int whichButton) {
                    Log.d(MSG_TAG, "Yes pressed");
                    Log.d("경로명","경로명 : " + downloadFileUrl + " 파일명 : " + fileName );
                    downloadUpdate(downloadFileUrl, fileName, localpath4down);
                }
            });          
        } else {              
            dialog.setNeutralButton("확인",new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int whichButton) {
                    Log.d(MSG_TAG, "Ok pressed");
                }
            });
        }
        dialog.show();
//dialog.setCancelable(false); // back 키 무효. 동작 안함
        //dialog.setCanceledOnTouchOutside(false); // dialog 바깥 클릭 무효. 동작 안함

    
    //다운로드 받은 앱을 설치, 이전 실행 앱 종료
    public void downloadUpdate(final String downloadFileUrl, final String fileName,final String localpath) {
        new Thread(new Runnable(){
            public void run(){
                Message msg = Message.obtain();
                msg.what = MESSAGE_DOWNLOAD_STARTING;
                msg.obj = localpath + fileName ;
              
                File apkFile = new File ( localpath + fileName );
                Log.d("downloadUpdate","경로1:"+ localpath + fileName  );
                viewUpdateHandler.sendMessage(msg);
              
                downloadUpdateFile(downloadFileUrl, fileName, localpath);              
              
                //다운로드 받은 패키지를 인스톨한다.
                Intent intent = new Intent(Intent.ACTION_VIEW);
                intent.setDataAndType(Uri.fromFile(apkFile),"application/vnd.android.package-archive");
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                startActivity(intent);


               /*
               * 안드로이드 프로세스는  단지 finish() 만 호출 하면 죽지 않는다.
               * 만약 프로세스를 강제로 Kill 하기위해서는 화면에 떠있는 Activity를 BackGround로 보내고
               * 강제로 Kill하면 프로세스가  완전히 종료가 된다.
               * 종료 방법에 대한 Source는 아래 부분을 참조 하면 될것 같다.
               */
                moveTaskToBack(true);
                finish();
                android.os.Process.sendSignal(android.os.Process.myPid(), android.os.Process.SIGNAL_KILL);
          }
        }).start();
       }
      
    public Handler viewUpdateHandler = new Handler(){
        public void handleMessage(Message msg) {
            switch(msg.what) {
                case MESSAGE_DOWNLOAD_STARTING :
                    Log.d(MSG_TAG, "프로그레스바 시작");
                    progressBar.setIndeterminate(true);
                    progressTitle.setText((String)msg.obj + " 다운로드");
                    progressText.setText("시작중...");
                    downloadUpdateLayout.setVisibility(View.VISIBLE);
                    break;
                case MESSAGE_DOWNLOAD_PROGRESS :
                    progressBar.setIndeterminate(false);
                    progressText.setText(msg.arg1 + "k /" + msg.arg2 + "k");
                    progressTitle.setText("최신 버전을 다운로드 하고 있습니다.");
                    progressBar.setProgress(msg.arg1*100/msg.arg2);
                    break;
                case MESSAGE_DOWNLOAD_COMPLETE :
                    Log.d(MSG_TAG, "다운로드 완료.");
                    progressText.setText("");
                    progressTitle.setText("");
                    downloadUpdateLayout.setVisibility(View.GONE);
                    break;              
            }
            super.handleMessage(msg);
        }
    };
     
    public boolean downloadUpdateFile(String downloadFileUrl, String destinationFilename, String localPath) {
        if (Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED) == false) {
            return false;
        }
      
        File downloadDir = new File( localPath );
        Log.d("DOWNLOAD","다운로드중");
        if (downloadDir.exists() == false) {
            downloadDir.mkdirs();
        } else {
            File downloadFile = new File( localPath + destinationFilename );
            if (downloadFile.exists()) {
                downloadFile.delete();
            }
        }
        return this.downloadFile(downloadFileUrl, localPath, destinationFilename );
    }    
    
    //파일 다운로드 과정 표시
    public boolean downloadFile(String url, String destinationDirectory, String destinationFilename ) {
        boolean filedownloaded = true;
        HttpClient client = new DefaultHttpClient();
        HttpGet request = new HttpGet(String.format(url));
        Message msg = Message.obtain();
        
        try {
            HttpResponse response = client.execute(request);
            StatusLine status = response.getStatusLine();
            Log.d(MSG_TAG, "Request returned status " + status);
               
            if (status.getStatusCode() == 200) {
                HttpEntity entity = response.getEntity();
                InputStream instream = entity.getContent();
                int fileSize = (int)entity.getContentLength();
                FileOutputStream out = new FileOutputStream(new File(destinationDirectory + destinationFilename));
                byte buf[] = new byte[8192];
                int len;
                int totalRead = 0;


                while((len = instream.read(buf)) > 0) {
                    msg = Message.obtain();
                    msg.what = MESSAGE_DOWNLOAD_PROGRESS;
                    totalRead += len;
                    msg.arg1 = totalRead / 1024;
                    msg.arg2 = fileSize / 1024;
                    viewUpdateHandler.sendMessage(msg);
                    out.write(buf,0,len);
                }
                   out.close();
            } else {
                throw new IOException();
            }
        } catch (IOException e) {  
            filedownloaded = false;
        }
        msg = Message.obtain();
        msg.what = MESSAGE_DOWNLOAD_COMPLETE;
        viewUpdateHandler.sendMessage(msg);
        return filedownloaded;
    }  


}

 

 

 

activity_autoupdate.xml

 

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent" android:layout_gravity="center" >
    <TextView        android:id="@+id/textVersion"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_gravity="center"        android:text="Version"        android:textSize="14sp" />
    <LinearLayout        android:layout_width="wrap_content"        android:layout_height="wrap_content" android:layout_gravity="center_vertical">                <RelativeLayout android:id="@+id/layoutDownloadUpdate"            android:layout_width="match_parent"            android:layout_height="60dp"            android:visibility="gone"            android:layout_gravity="center_horizontal"            android:gravity="center"            android:background="#A0909090">
            <TextView                android:id="@+id/progressTitle"                android:layout_width="match_parent"                 android:layout_height="wrap_content"                 android:layout_alignParentTop="true"                android:paddingLeft="5dp"                android:paddingRight="5dp"                android:text=""                android:textStyle="bold"                android:textColor="#000000" />
            <ProgressBar                android:id="@+id/progressBar"                android:layout_width="match_parent"                android:layout_height="wrap_content"                style="?android:attr/progressBarStyleHorizontal"                android:paddingTop="22dp"                android:paddingLeft="5dp"                android:paddingRight="5dp"                android:layout_alignParentTop="true"                android:max="100" />
            <TextView                  android:id="@+id/progressText"                android:layout_width="match_parent"                android:layout_height="wrap_content"                 android:paddingTop="40dp"                android:paddingLeft="5dp"                android:paddingRight="5dp"                android:layout_alignParentTop="true"                android:text=""                android:textStyle="bold"                android:textColor="#000000" />
        </RelativeLayout>
    </LinearLayout>  
</FrameLayout>

 

 

 

updateview.xml (dialog 창)

 

<ScrollView     xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent" >         <LinearLayout        android:orientation="vertical"        android:layout_width="match_parent"        android:layout_height="wrap_content">
      <TextView              android:id="@+id/updateMessage"              android:layout_width="match_parent"              android:layout_height="match_parent"              android:autoLink="web"              android:paddingLeft="15dp"              android:paddingRight="15dp"              android:paddingTop="15dp"              android:text=""              android:textColor="#00ffff"              android:textStyle="bold" />       <TextView               android:text="Update"               android:id="@+id/updateNowText"               android:layout_width="match_parent"                android:layout_height="wrap_content"               android:paddingTop="15dp"               android:paddingLeft="15dp"               android:paddingRight="15dp"               android:textStyle="bold"               android:textColor="#000000" />  
    </LinearLayout>
</ScrollView>

 

 

[출처] http://blog.daum.net/wonky12/1401710

 

반응형

댓글()

heal instance info cache interval

리눅스/OpenStack|2017. 9. 25. 13:38
반응형

1분단위로 인스턴스 네트워크 상태체크 

# tail -f /var/log/neutron/neutron-server.log 

2017-09-25 10:19:53.822 24829 INFO neutron.wsgi [req-448162bd-dc52-45d1-b2c7-8bb9fe59bb1f 1c3b76bac514414ba0c7ac2233b5ebea 5440c3eab4604978b676ffe6f748a96e - - -] 172.16.217.31 - - [25/Sep/2017 10:19:53] "GET /v2.0/ports.json?tenant_id=12c23eb3174d44e391389c0e69e2a4b8&device_id=3591e5e8-cc80-4e7f-a49f-e915e86faf95 HTTP/1.1" 200 1073 3.262539

2017-09-25 10:19:54.136 24829 INFO neutron.wsgi [req-f3316317-df05-4b2f-b4bb-8598d82ba894 1c3b76bac514414ba0c7ac2233b5ebea 5440c3eab4604978b676ffe6f748a96e - - -] 172.16.217.31 - - [25/Sep/2017 10:19:54] "GET /v2.0/networks.json?id=9a6bafb9-aae2-46b8-b72c-26abd4ea41ec HTTP/1.1" 200 857 0.309153

2017-09-25 10:19:54.400 24829 INFO neutron.wsgi [req-494bde0e-8b4e-4466-877a-49c616c276a7 1c3b76bac514414ba0c7ac2233b5ebea 5440c3eab4604978b676ffe6f748a96e - - -] 172.16.217.31 - - [25/Sep/2017 10:19:54] "GET /v2.0/floatingips.json?fixed_ip_address=172.16.0.13&port_id=ef99e958-676d-420f-8715-98040783e659 HTTP/1.1" 200 609 0.260283

2017-09-25 10:19:54.737 24829 INFO neutron.wsgi [req-007a921b-385e-4fd5-80fb-51288acce0ee 1c3b76bac514414ba0c7ac2233b5ebea 5440c3eab4604978b676ffe6f748a96e - - -] 172.16.217.31 - - [25/Sep/2017 10:19:54] "GET /v2.0/subnets.json?id=67bbebe9-0ce3-4b49-b36b-43495101763d HTTP/1.1" 200 785 0.332609

2017-09-25 10:19:58.015 24829 INFO neutron.wsgi [req-9299fcb2-9323-4cdf-bf5f-20db2a4a1000 1c3b76bac514414ba0c7ac2233b5ebea 5440c3eab4604978b676ffe6f748a96e - - -] 172.16.217.31 - - [25/Sep/2017 10:19:58] "GET /v2.0/ports.json?network_id=9a6bafb9-aae2-46b8-b72c-26abd4ea41ec&device_owner=network%3Adhcp HTTP/1.1" 200 1076 3.274809



기본 interval 수정 (60 --> 120)

ComputeNode

# vim /usr/lib/python2.7/dist-packages/nova/compute/manager.py

165     cfg.IntOpt("heal_instance_info_cache_interval",

166                default=120,



# cp /usr/lib/python2.7/dist-packages/nova/compute/manager.py /usr/lib/python2.7/dist-packages/nova/compute/manager.py_20170925

# sed -i "166s/default=60/default=120/g" /usr/lib/python2.7/dist-packages/nova/compute/manager.py

# /etc/init.d/nova-compute restart 



[출처] https://chonnom.com/bbs/board.php?bo_table=OpenStack&wr_id=544&page=0&sca=&sfl=wr_subject%7C%7Cwr_content&stx=&sst=&sod=&spt=0&page=0

반응형

댓글()

aggregate 에서 컴퓨트 노드 추가, 제거하기

리눅스/OpenStack|2017. 9. 20. 14:41
반응형

# nova aggregate-list 

+----+----------------------+-------------------+ 

| Id | Name                 | Availability Zone | 

+----+----------------------+-------------------+ 

| 1  | share-zone1          | vCore             | 

| 3  | nonshare-zone1       | rCore             | 

| 4  | nonshare-8core-zone1 | rCore-8           | 

| 5  | sysdocu-group            | sysdocu               |

+----+----------------------+-------------------+ 


예제에서 nonshare 는 rCore 를 말합니다.

현재는 share, rCore, rCore(8core전용) 세 그룹이 있습니다.

각 그룹마다 어떤 컴퓨트 노드들이 포함되어있는지 확인하는 명령은 아래와 같습니다.


# nova aggregate-details 5    // ID 값 5번 (sysdocu) 에 포함된 리스트 출력


이제 아래와 같이 sysdocu 그룹에서 컴퓨트 노드를 빼고 rCore 그룹에 hl-217-50 컴퓨트 노드를 추가합니다.


# nova aggregate-remove-host 5 hl-217-50


# nova aggregate-add-host 3 hl-217-50


rCore 그룹에 포함되었으며 개통 가능한 상태인지 확인하는 방법은 아래와 같습니다.


# nova service-list |grep hl-217-50

| 216 | nova-compute     | hl-217-50  | rCore      | disabled  | up    | 2017-09-20T05:39:42.000000 | -               |


# nova service-enable hl-217-50 nova-compute    // 활성화 시키기

# nova service-disable hl-217-50 nova-compute    // 비활성화 시키기

반응형

댓글()

컴퓨트 노드 변경 (마이그레이션 migration) 하기

리눅스/OpenStack|2017. 9. 20. 14:37
반응형

VM 을 다른 컴퓨트 노드로 옮기는 방법입니다.


1. VM 이 위치한 컴퓨트 노드와 flavor 확인

# nova show cb8aa272-736d-43f4-abf0-596da4664234 |grep -E 'hyper|flavor'

| OS-EXT-SRV-ATTR:hypervisor_hostname  | hl-217-50                                                  |

| flavor                               | rCore.P1 (95d11601-728d-43b5-804d-4612673a30eb)            |


2. flavor 변경 (resize)

# nova resize cb8aa272-736d-43f4-abf0-596da4664234 vCore.V1


* 참고 flavor list 보기

# nova flavor-list


3. 확인

# nova show cb8aa272-736d-43f4-abf0-596da4664234 |grep -E 'hyper|flavor'

| OS-EXT-SRV-ATTR:hypervisor_hostname  | hl-217-51                                                  |

| flavor                               | vCore.V1 (e4b1a4be-9f72-42ce-b1d3-67e4546ad450)            |


조금 기다리면 컴퓨트 노드와 flavor 가 바뀐것이 확인 됩니다.

혹시 flavor 만 변경되고 컴퓨트 노드가 변경 안된다면 아래와 같은 명령으로 대체해서 테스트가 가능합니다.


(참고)

# nova migrate  cb8aa272-736d-43f4-abf0-596da4664234    // 껐다 이동후 켜기

# nova live-migration  cb8aa272-736d-43f4-abf0-596da4664234 hl-217-51    // 운영중 이동 & 컴퓨트 노드 지정


nova live-migration 이 안될 경우

nova live-migration --block-migrate 옵션을 추가하여 실행하면 됩니다.

그리고 잘 실행이 됐는지 nova show [VM ID] |grep status 로 확인해봅니다.

반응형

댓글()

네비게이션바 (navigation bar) 상태 확인

프로그래밍/Android (Java)|2017. 9. 14. 13:39
반응형

import android.view.KeyEvent;

import android.view.KeyCharacterMap;



boolean hasBackKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);

boolean hasHomeKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_HOME);


if (hasBackKey && hasHomeKey) {

    // no navigation bar, unless it is enabled in the settings

} else {

    // 99% sure there's a navigation bar

}



[출처] https://stackoverflow.com/questions/16092431/check-for-navigation-bar

반응형

댓글()

Checkbox 하나만 선택되게 하기

반응형

오늘 업무요청을 받으며 라디오버튼을 체크박스로

변경해달라는 요청을 받았다.

라디오버튼은 이름이 같으면 그중에 하나만 선택되는데..

체크박스는 그렇지 않고 다중 선택이 되는 까닭에..

그냥 바꾸어주면 프로그램상 상당한문제이기도 하고..

업무 요청한곳에서도 다중선택의 문제라기보다는


라디오버튼은 한번 선택하면 그 중에 한가지를 택해야하니..

체크박스로 아무것도 선택 안하는 경우를 염두한거 같다..


서문이 길었는데.. 간단하게 자바스크립트 이벤트를 걸어주었다.


function doOpenCheck(chk){

    var obj = document.getElementsByName("aaa");

    for(var i=0; i<obj.length; i++){

        if(obj[i] != chk){

            obj[i].checked = false;

        }

    }

}


그리고 체크박스에는 onclick 이벤트를 걸어주면 되겠다.


<input name="aaa" type="checkbox" value="1" onclick="doOpenCheck(this);">aaa <br />

<input name="aaa" type="checkbox" value="2" onclick="doOpenCheck(this);">bbb <br />

<input name="aaa" type="checkbox" value="3" onclick="doOpenCheck(this);">ccc <br />



'this' 를 넘겨주어서 클릭되어진 객체의 값을 넘겨주고 'aaa' 로 값을 가져온것과 비교 하면서

클릭된 객체와 일치 하지 않는것들은 체크를 해제해주면 되겠다.



출처: http://canworld42.tistory.com/25 [깨어 있는 세상]

반응형

댓글()