gksu 사용법 - root 권한으로 GUI 어플 실행하기

|

라즈베리파이 뿐만 아니라 우분투(Ubuntu)에서도 동일하게 적용되는 방법입니다.

터미널에서 GUI에 Root 권한을 주는 명령어는 gksu입니다.

예를 들어 파일 탐색기 GUI를 Root 권한으로 실행하는 방법은 터미널에서

gksu pcmanfm

으로 실행하면 됩니다. (pcmanfm이 라즈베리파이용 기본 파일 탐색기입니다.)

만약 텍스트에디터를 열고 싶으시면

gksu leafpad

라고 실행하시면 됩니다. (leafpad가 라즈베리파이용 기본 텍스트에디터기입니다.)

Ubuntu Server에 Desktop GUI 설치하기

|

Ubuntu Server

Ubuntu Server는 Desktop GUI가 없고, 터미널만 존재하는 모드입니다. 여기에 Desktop GUI를 설치하기는 방법입니다.


설치 명령어

먼저 아래 명령어를 이용해서 apt-get을 업데이트해줍니다.

sudo apt-get update
sudo apt-get upgrade

그리고 아래 명령어를 이용해서 ubuntu-desktop를 설치해줍니다.

apt-get install ubuntu-desktop

위와 같이 설치할 경우, 파이어폭스나 오픈오피스 등의 기본적인 프로그램들도 같이 설치되는데, 대략 700MB 정도의 설치파일을 다운로드하게 됩니다. (설치후에는 더 큰 용량을 차지합니다.)

만약 기본 프로그램들은 제외하고 Ubutu Desktop 최소 설치를 진행하려면 아래와 같은 명령어를 사용하면 됩니다. 이 때는 약 170MB 정도의 설치파일을 다운로드합니다.

sudo apt-get install --no-install-recommends ubuntu-desktop


실행 방법

Ubuntu Desktop를 실행하는 명령어는 startx 입니다.


추가 패키지 설치

만약 최소 설치로 진행한 경우 추가적인 패키지를 설치해주도록 합시다.

상단 메뉴 시간 추가

sudo apt-get install indicator-datetime

hud service not connected 오류 해결

sudo apt-get install indicator-appmenu-tools

볼륨조절 아이콘 추가

sudo apt-get install indicator-applet-complete

Git 사용 방법 요약

|

다양한 형상 관리 툴이 나왔지만, 요즘은 Git을 많이 사용하고 있는 것 같습니다.

Git은 크게 master 저장소를 가지는 서버와 master 저장소의 완벽한 사본을 가지는 클라이언트 저장소로 구성되어 있습니다.

따라서 로컬 저장소 사용 방법과 원격 저장소 사용 방법을 나누어서 설명하도록 하겠습니다.


로컬 저장소 사용을 위한 명령어

로컬 저장소에서는 다음과 같은 명령어를 사용할 수 있습니다.

명령어 설명
git init 현재 위치에 git 저장소를 생성
git add 파일이름 git에 파일을 등록
git commit git 저장소에 변경 이력 제출
git status git 저장소의 상태 확인

만약 큰 수정을 위해 별도로 브랜치(branch)를 생성해서 작업을해야 한다면 다음 명령어를 이용할 수 있습니다.

명령어 설명
git branch 이름 새로운 브랜치를 생성
git checkout 이름 해당 브랜치로 변경
git merge 이름 현재 브랜치에 ‘이름’의 브랜치 내용을 정합

브랜치를 활용한 작업 흐름

image


원격 저장소 사용을 위한 명령어

명령어 설명
git clone url 원격 저장소(URL)의 내용을 로컬 저장소에 복사
git remote 원격 저장소와 로컬 저장소를 연결
git push 로컬 저장소의 내용을 원격 저장소에 업로드
git fetch 로컬 저장소와 원격 저장소의 변경 내용이 다를 때 이를 비교/대조. git merge 명령어와 함께 최신 데이터를 반영하거나 충돌 문제를 해결
git pull 로컬 저장소의 내용을 원격 저장소의 최신 내용으로 업데이트

예를 들면, git clone은 다음과 같은 형태로 사용 가능합니다.

git clone https://github.com/snowdeer/bitcoin

git push의 경우는 다음과 같이 사용할 수 있습니다.

git push [원격 저장소 별칭] [로컬 브랜치 이름]

원격 저장소에 로컬 저장소의 모든 브랜치를 업로드할 때는 아래와 같이 사용할 수 있습니다.

git push origin --all


fetch와 pull

원격 저장소의 내용을 로컬 저장소로 가져와서 정합하는 방법은 크게 git fetchgit pull 두 가지가 있습니다.

pull의 경우는 원격 저장소의 내용을 가져오면서 로컬 저장소에 자동으로 정합을 해줍니다. 다만, 이 경우는 어떤 내용이 변경되었고 정합되었는지 확인하기가 어렵습니다. 따라서 원격 저장소에서 무조건 pull로 데이터를 가져오는 것은 추천하지 않는 방법입니다.

fetch의 경우는 원격 저장소의 commit을 가져와서 로컬 저장소에서 이를 확인한다음 수동으로 정합할 수 있습니다.

경우에 따라 fetchpull을 적당히 혼용해서 사용하면 될 것 같습니다.

ini 파일 읽어들이기

|

ini 파일은 각종 설정값 등을 정의한 설정 파일로, de facto 표준입니다. 단순 텍스트 파일로 되어 있어서 수정이 편리합니다.


ini 파일 예제

ini 파일은 다음과 같은 형태로 되어 있습니다.

; Test ini file

[version]                   ; INI file version
version = 1.02
version_name = Hello, snowdeer

[device_config]             ; Device configuration
mic_enabled = false
speaker_enabled = false
camera_enabled = true
motion_enabled = true


다음 헤더 파일만 프로젝트에 첨부하면 됩니다.

INIReader.h


#ifndef __INI_H__
#define __INI_H__

#ifdef __cplusplus
extern "C" {
#endif

#include <stdio.h>

typedef int (*ini_handler)(void *user, const char *section,
                           const char *name, const char *value);

typedef char *(*ini_reader)(char *str, int num, void *stream);

int ini_parse(const char *filename, ini_handler handler, void *user);

int ini_parse_file(FILE *file, ini_handler handler, void *user);

int ini_parse_stream(ini_reader reader, void *stream, ini_handler handler,
                     void *user);

#ifndef INI_ALLOW_MULTILINE
#define INI_ALLOW_MULTILINE 1
#endif

/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of
   the file. See http://code.google.com/p/inih/issues/detail?id=21 */
#ifndef INI_ALLOW_BOM
#define INI_ALLOW_BOM 1
#endif

/* Nonzero to allow inline comments (with valid inline comment characters
   specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match
   Python 3.2+ configparser behaviour. */
#ifndef INI_ALLOW_INLINE_COMMENTS
#define INI_ALLOW_INLINE_COMMENTS 1
#endif
#ifndef INI_INLINE_COMMENT_PREFIXES
#define INI_INLINE_COMMENT_PREFIXES ";"
#endif

/* Nonzero to use stack, zero to use heap (malloc/free). */
#ifndef INI_USE_STACK
#define INI_USE_STACK 1
#endif

/* Stop parsing on first error (default is to keep parsing). */
#ifndef INI_STOP_ON_FIRST_ERROR
#define INI_STOP_ON_FIRST_ERROR 0
#endif

/* Maximum line length for any line in INI file. */
#ifndef INI_MAX_LINE
#define INI_MAX_LINE 200
#endif

#ifdef __cplusplus
}
#endif

/* inih -- simple .INI file parser

inih is released under the New BSD license (see LICENSE.txt). Go to the project
home page for more info:

https://github.com/benhoyt/inih

*/

#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS
#endif

#include <stdio.h>
#include <ctype.h>
#include <string.h>

#if !INI_USE_STACK
#include <stdlib.h>
#endif

#define MAX_SECTION 50
#define MAX_NAME 50

/* Strip whitespace chars off end of given string, in place. Return s. */
inline static char *rstrip(char *s) {
  char *p = s + strlen(s);
  while (p > s && isspace((unsigned char) (*--p)))
    *p = '\0';
  return s;
}

/* Return pointer to first non-whitespace char in given string. */
inline static char *lskip(const char *s) {
  while (*s && isspace((unsigned char) (*s)))
    s++;
  return (char *) s;
}

/* Return pointer to first char (of chars) or inline comment in given string,
   or pointer to null at end of string if neither found. Inline comment must
   be prefixed by a whitespace character to register as a comment. */
inline static char *find_chars_or_comment(const char *s, const char *chars) {
#if INI_ALLOW_INLINE_COMMENTS
  int was_space = 0;
  while (*s && (!chars || !strchr(chars, *s)) &&
      !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) {
    was_space = isspace((unsigned char) (*s));
    s++;
  }
#else
  while (*s && (!chars || !strchr(chars, *s))) {
        s++;
    }
#endif
  return (char *) s;
}

/* Version of strncpy that ensures dest (size bytes) is null-terminated. */
inline static char *strncpy0(char *dest, const char *src, size_t size) {
  strncpy(dest, src, size);
  dest[size - 1] = '\0';
  return dest;
}

/* See documentation in header file. */
inline int ini_parse_stream(ini_reader reader, void *stream, ini_handler handler,
                            void *user) {
  /* Uses a fair bit of stack (use heap instead if you need to) */
#if INI_USE_STACK
  char line[INI_MAX_LINE];
#else
  char* line;
#endif
  char section[MAX_SECTION] = "";
  char prev_name[MAX_NAME] = "";

  char *start;
  char *end;
  char *name;
  char *value;
  int lineno = 0;
  int error = 0;

#if !INI_USE_STACK
  line = (char*)malloc(INI_MAX_LINE);
    if (!line) {
        return -2;
    }
#endif

  /* Scan through stream line by line */
  while (reader(line, INI_MAX_LINE, stream) != NULL) {
    lineno++;

    start = line;
#if INI_ALLOW_BOM
    if (lineno == 1 && (unsigned char) start[0] == 0xEF &&
        (unsigned char) start[1] == 0xBB &&
        (unsigned char) start[2] == 0xBF) {
      start += 3;
    }
#endif
    start = lskip(rstrip(start));

    if (*start == ';' || *start == '#') {
      /* Per Python configparser, allow both ; and # comments at the
         start of a line */
    }
#if INI_ALLOW_MULTILINE
    else if (*prev_name && *start && start > line) {

#if INI_ALLOW_INLINE_COMMENTS
      end = find_chars_or_comment(start, NULL);
      if (*end)
        *end = '\0';
      rstrip(start);
#endif

      /* Non-blank line with leading whitespace, treat as continuation
         of previous name's value (as per Python configparser). */
      if (!handler(user, section, prev_name, start) && !error)
        error = lineno;
    }
#endif
    else if (*start == '[') {
      /* A "[section]" line */
      end = find_chars_or_comment(start + 1, "]");
      if (*end == ']') {
        *end = '\0';
        strncpy0(section, start + 1, sizeof(section));
        *prev_name = '\0';
      } else if (!error) {
        /* No ']' found on section line */
        error = lineno;
      }
    } else if (*start) {
      /* Not a comment, must be a name[=:]value pair */
      end = find_chars_or_comment(start, "=:");
      if (*end == '=' || *end == ':') {
        *end = '\0';
        name = rstrip(start);
        value = lskip(end + 1);
#if INI_ALLOW_INLINE_COMMENTS
        end = find_chars_or_comment(value, NULL);
        if (*end)
          *end = '\0';
#endif
        rstrip(value);

        /* Valid name[=:]value pair found, call handler */
        strncpy0(prev_name, name, sizeof(prev_name));
        if (!handler(user, section, name, value) && !error)
          error = lineno;
      } else if (!error) {
        /* No '=' or ':' found on name[=:]value line */
        error = lineno;
      }
    }

#if INI_STOP_ON_FIRST_ERROR
    if (error)
            break;
#endif
  }

#if !INI_USE_STACK
  free(line);
#endif

  return error;
}

/* See documentation in header file. */
inline int ini_parse_file(FILE *file, ini_handler handler, void *user) {
  return ini_parse_stream((ini_reader) fgets, file, handler, user);
}

/* See documentation in header file. */
inline int ini_parse(const char *filename, ini_handler handler, void *user) {
  FILE *file;
  int error;

  file = fopen(filename, "r");
  if (!file)
    return -1;
  error = ini_parse_file(file, handler, user);
  fclose(file);
  return error;
}

#endif /* __INI_H__ */


#ifndef __INIREADER_H__
#define __INIREADER_H__

#include <map>
#include <set>
#include <string>

// Read an INI file into easy-to-access name/value pairs. (Note that I've gone
// for simplicity here rather than speed, but it should be pretty decent.)
class IniReader {
 public:
  // Construct IniReader and parse given filename. See ini.h for more info
  // about the parsing.
  IniReader(std::string filepath);

  // Return the result of ini_parse(), i.e., 0 on success, line number of
  // first error on parse error, or -1 on file open error.
  int parseError();

  // Return the list of sections found in ini file
  std::set<std::string> sections();

  // getString a string value from INI file, returning default_value if not found.
  std::string getString(std::string section, std::string name,
                        std::string default_value);

  // getString an integer (long) value from INI file, returning default_value if
  // not found or not a valid integer (decimal "1234", "-1234", or hex "0x4d2").
  long getLong(std::string section, std::string name, long default_value);

  // getString a real (floating point double) value from INI file, returning
  // default_value if not found or not a valid floating point value
  // according to strtod().
  double getDouble(std::string section, std::string name, double default_value);

  // getString a boolean value from INI file, returning default_value if not found or if
  // not a valid true/false value. Valid true values are "true", "yes", "on", "1",
  // and valid false values are "false", "no", "off", "0" (not case sensitive).
  bool getBool(std::string section, std::string name, bool default_value);

 private:
  int _error;
  std::map<std::string, std::string> _values;
  std::set<std::string> _sections;
  static std::string makeKey(std::string section, std::string name);
  static int valueHandler(void *user, const char *section, const char *name,
                          const char *value);
};

#endif  // __INIREADER_H__


#ifndef __INIREADER__
#define __INIREADER__

#include <algorithm>
#include <cctype>
#include <cstdlib>

using std::string;

inline IniReader::IniReader(string filepath) {
  _error = ini_parse(filepath.c_str(), valueHandler, this);
}

inline int IniReader::parseError() {
  return _error;
}

inline std::set<string> IniReader::sections() {
  return _sections;
}

inline string IniReader::getString(string section, string name, string default_value) {
  string key = makeKey(section, name);
  return _values.count(key) ? _values[key] : default_value;
}

inline long IniReader::getLong(string section, string name, long default_value) {
  string valstr = getString(section, name, "");
  const char *value = valstr.c_str();
  char *end;
  // This parses "1234" (decimal) and also "0x4D2" (hex)
  long n = strtol(value, &end, 0);
  return end > value ? n : default_value;
}

inline double IniReader::getDouble(string section, string name, double default_value) {
  string valstr = getString(section, name, "");
  const char *value = valstr.c_str();
  char *end;
  double n = strtod(value, &end);
  return end > value ? n : default_value;
}

inline bool IniReader::getBool(string section, string name, bool default_value) {
  string valstr = getString(section, name, "");
  // Convert to lower case to make string comparisons case-insensitive
  std::transform(valstr.begin(), valstr.end(), valstr.begin(), ::tolower);
  if (valstr == "true" || valstr == "yes" || valstr == "on" || valstr == "1")
    return true;
  else if (valstr == "false" || valstr == "no" || valstr == "off" || valstr == "0")
    return false;
  else
    return default_value;
}

inline string IniReader::makeKey(string section, string name) {
  string key = section + "=" + name;
  // Convert to lower case to make section/name lookups case-insensitive
  std::transform(key.begin(), key.end(), key.begin(), ::tolower);
  return key;
}

inline int IniReader::valueHandler(void *user, const char *section, const char *name,
                                   const char *value) {
  IniReader *reader = (IniReader *) user;
  string key = makeKey(section, name);
  if (reader->_values[key].size() > 0)
    reader->_values[key] += "\n";
  reader->_values[key] += value;
  reader->_sections.insert(section);
  return 1;
}

#endif  // __INIREADER__


사용법

#include "IniReader.h"

#include <iostream>

int main(void) {

  IniReader reader("test.ini");

  if (reader.parseError() < 0) {
    std::cout << "Can't load 'test.ini'\n";
    return 1;
  }

  double version = reader.getDouble("version", "version", -1);
  string version_name = reader.getString("version", "version_name", "");

  printf("Version:%.2f, (%s)\n", version, version_name.c_str());

  bool mic_enabled = reader.getBool("device", "mic_enabled", false);
  bool speaker_enabled = reader.getBool("device", "speaker_enabled", false);
  bool camera_enabled = reader.getBool("device", "camera_enabled", false);
  bool motion_enabled = reader.getBool("device", "motion_enabled", false);

  printf("mic_enabled : %d, speaker_enabled: %d, camera_enabled:%d, motion_enabled:%d\n",
         mic_enabled, speaker_enabled, camera_enabled, motion_enabled);

  return 0;
}

Linux에서 외부 명령어 실행하는 동안 대기하기

|

외부 명령어 실행하는 동안 대기하기

리눅스에서 외부 명령어를 실행하는 함수는 execlp 입니다. 외부 명령어를 호출하기 때문에 해당 명령어가 어떻게 실행되고 있는지, 종료가 되었는지 알기가 힘든데 fork()wait()를 이용하면 외부 명령어의 종료 시점을 알 수 있습니다.

#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <sys/wait.h>

int main(void) {
  int pid;

  pid = fork();

  if (pid < 0) {
    printf("A fork error has occurred.\n");
    exit(-1);
  }

  if (pid == 0) {
    printf("Forking is successful.\n");
    execlp("/bin/ls", "ls", nullptr);
    exit(127);
  } else {
    printf("This is the parent.\n");    
    int status;
    waitpid(pid, &status, 0);
    printf("The child just ended\n");
  }

  return 0;
}


system() 함수

system() 함수는 내부적으로 fork(), exec(), waitpid()으로 이루어져 있기 때문에 보다 쉽게 사용할 수 있습니다.

#include <stdlib.h>

int system(const char *cmd);

명령이 정상적으로 수행될 경우 127 값을 리턴하며, 오류가 난 경우 -1을 리턴합니다. system() 함수의 내부는 다음 코드와 비슷합니다.

#include <stdio>
#include <errno>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int system(const char *cmd) {
  pid_t pid;
  int status;

  if ((pid = fork()) < 0) {
    status = -1;
  }
  else if (pid == 0) {
    execl("/bin/sh", "sh", "-c", cmd, (char *) 0);
    _exit(127);
  } else {
    while(waitpid(pid, &status, 0) < 0) {
      if(errno != EINTR) {
        status = -1;
        break;
      }
    }
  }

  return status;
}