'퍼온글'에 해당되는 글 71건

  1. 2012/04/18 조재혁 [퍼온글] glibc 공유 라이브러리 심볼 버전 관리
  2. 2012/04/18 조재혁 [퍼온글] 링커 스크립트(Linker Scripts)
  3. 2012/04/17 조재혁 [퍼온글] 쉘 프로그래밍 강좌
  4. 2012/04/17 조재혁 [퍼온글] GNU Libtool 2.4.2
  5. 2012/04/10 조재혁 [퍼온글] "libcrypto.so: no version info available (required by git)"
  6. 2012/04/09 조재혁 [퍼온글] Berkeley Packet Filter (BPF) syntax
  7. 2012/04/05 조재혁 [퍼온글] Software/Xserver/InstallGuide
  8. 2012/04/05 조재혁 [퍼온글] X-Window에서 ARGB visual 을 얻어오는 함수
  9. 2012/04/05 조재혁 [퍼온글] X.Org/Transparency From Gentoo Linux Wiki
  10. 2012/04/04 조재혁 [퍼온글] NAT64—Stateless versus Stateful
  11. 2012/02/10 조재혁 [퍼온글] Gnu Toolchain/Compatibility Matrix
  12. 2012/02/08 조재혁 [퍼온글] HARDWARE b2c2 From Gentoo Linux Wiki
  13. 2012/01/09 조재혁 [퍼온글] An O(ND) Difference Algorithm and Its Variations의 핵심.
  14. 2011/12/27 조재혁 [퍼온글] Zero Copy에 대하여...
  15. 2011/12/26 조재혁 [퍼온글] git 튜토리얼 메뉴얼 페이지
  16. 2011/09/15 조재혁 [퍼온글] ppTp VPN Linux client 설정 방법
  17. 2011/09/01 조재혁 [퍼온글] git 튜토리얼 메뉴얼 페이지
  18. 2011/09/01 조재혁 [퍼온글] Windows NDIS Intermediate 드라이버 개발
  19. 2011/07/20 조재혁 UUID (Universally Unique ID) 란?
  20. 2011/06/22 조재혁 [퍼온글] Curl에 대해서
  21. 2011/06/16 조재혁 [퍼온글] fanotify API
  22. 2011/06/16 조재혁 [퍼온글] Inotify를 이용한 Linux 파일 시스템 모니터
  23. 2011/06/15 조재혁 [퍼온글] 홈 미들웨어(Home Middle-Ware)
  24. 2011/06/15 조재혁 [퍼온글] 리눅스 에서 UPNP 동작 시키기
  25. 2011/06/15 조재혁 [퍼온글] uPnP의 구성요소 동작 순서
  26. 2011/06/15 조재혁 [퍼온글] SOAP Version 1.2 Part 0: Primer
  27. 2011/06/13 조재혁 [퍼온글] SConstruct basic (SCons)
  28. 2011/02/17 조재혁 [퍼온글] AES(Rijndael)
  29. 2011/02/17 조재혁 [퍼온글] 암호화의 패딩과 운영모드
  30. 2011/01/12 조재혁 [퍼온글] Using as The GNU Assembler - January 1994

본 내용은 "http://www.ibm.com/developerworks/kr/library/l-lpic1-v3-101-3/index.html" 에서 발췌한 것임을 밝힙니다.




개요

이 기사에서는 Linux 시스템을 종료하거나 다시 부팅하고, 시스템 중단을 사용자에게 경고하고, 단일 사용자 모드 또는 약간 제한적인 실행 레벨로 전환하는 방법을 학습한다. 그 내용은 다음과 같다.

  • 기본 실행 레벨 설정
  • 실행 레벨 간 변경
  • 단일 사용자 모드로 변경
  • 명령행에서 시스템 종료 또는 다시 부팅
  • 사용자에게 다른 실행 레벨로 전환 등을 포함한 주요 시스템 이벤트에 대한 경보 제공
  • 올바르게 프로세스 종료

별도로 언급하지 않는 한 이 기사의 예제에서는 2.6.26 커널의 Fedora 8 시스템을 사용한다. Upstart 예제에서는 2.6.34 커널의 Fedora 13 또는 2.6.35 커널의 Ubuntu 10.10을 사용한다. 다른 시스템에 대한 결과는 다를 수 있다.

이 기사를 통해 Linux Professional Institute's Junior Level Administration(LPIC-1) 시험 기초 과정, Topic 101의 Objective 101.3을 준비할 수 있다. 이 목적의 가중치는 3이다.

전제조건

이 시리즈 기사를 통해 많은 것을 배우려면 Linux에 대한 기본적인 지식이 있어야 하고 이 기사에 있는 명령을 실습할 수 있는 Linux 시스템을 다루는 데 익숙해야 한다. 때때로 같은 프로그램이라도 버전이 다르면 출력 형식이 다르므로, 실습 결과가 이 기사에서 보여주는 목록 및 그림과 항상 정확히 같지는 않을 수도 있다. 특히, 최신 upstart 시스템에서는 기존 System V init 프로세스의 사용자에게 익숙했던 많은 부분이 변경되고 있다(자세한 정보는 Init과 Upstart 비교 참조). 이 기사에서는 특별히 기존 System V init 프로세스에 중점을 두고서 upstart의 차이점을 간단히 살펴본다.

실행 레벨

Ian과 연락하기

Ian은 가장 인기있고 많은 글을 쓰는 저자 중 한 명이다. developerWorks에 실린 Ian의 모든 기사를 살펴보자. My developerWorks에서 Ian의 프로파일을 볼 수 있으며 Ian과 다른 저자 및 그를 좋아하는 여러 독자와 연락할 수 있다.

실행 레벨은 Linux 시스템의 현재 상태(또는 실행 레벨)에서 수행할 수 있는 태스크를 정의한다. 모든 Linux 시스템에서는 세 가지 기본 실행 레벨과 함께 정상적인 연산을 위한 하나 이상의 실행 레벨을 지원한다. 기본 실행 레벨은 표 1과 같다.


표 1. Linux 기본 실행 레벨
레벨용도
0시스템 종료(또는 정지)
1단일 사용자 모드, 일반적으로 s 또는 S라는 별명 사용
6시스템 다시 부팅

기본 실행 레벨 이외의 실행 레벨 사용법은 배포판마다 다르다. 일반적인 사용법 세트는 표 2와 같다.


표 2. 기타 일반적인 Linux 실행 레벨
레벨용도
2네트워크 기능을 제외한 다중 사용자 모드
3네트워크 기능을 포함한 다중 사용자 모드
5네트워크 기능 및 X Window System을 포함한 다중 사용자 모드

Slackware 배포판에서는 실행 레벨 5대신 4를 X Window System을 실행하는 전체 시스템에 사용한다. Debian 계열(예: Ubuntu)에서는 다중 사용자 모드에 단일 실행 레벨을 사용하며, 일반적으로 실행 레벨 2를 사용한다. 사용하는 배포판의 설명서를 참조하기 바란다.

기본 실행 레벨

Linux 시스템이 시작될 때 /etc/inittab의 id: 항목에서 기본 실행 레벨이 결정된다. 리스트 1에서는 Fedora 8 또는 openSUSE 11.2와 같은 시스템의 일반적인 항목을 보여 준다. 이 두 시스템에서는 X Window System에 실행 레벨 5를 사용한다.


리스트 1. /etc/inittab의 기본 실행 레벨
                    
[root@pinguino ~]# grep "^id:" /etc/inittab
id:5:initdefault:

시스템을 다른 실행 레벨(예: 실행 레벨 3)에서 시작하려면 이 값을 편집한다.

실행 레벨 변경하기

여러 가지 방법으로 실행 레벨을 변경할 수 있다. 영구적으로 변경하려는 경우 위에서 본 것처럼 /etc/inittab을 편집하여 기본 레벨을 변경할 수 있다.

한 번의 부팅 동안에만 다른 실행 레벨로 시스템을 실행하는 것도 가능하다. 예를 들어, 방금 전 새 커널을 설치했는데 시스템을 새 커널로 부팅한 후 X Window System을 시작하기 전에 일부 커널 모듈을 빌드해야 하는 상황을 가정해 보자. 이 작업을 수행하기 위해 시스템을 실행 레벨 3으로 실행할 수 있다. 부트 시간에 커널 행(GRUB)을 편집하거나 선택한 시스템 이름(LILO) 뒤에 매개변수를 추가하여 이를 수행할 수 있다. 한 자릿수 숫자를 사용하여 원하는 실행 레벨을 지정한다(이 경우에는 3). 여기에서는 GRUB 예제를 사용하여 이 프로세스를 설명한다. /boot/grub/menu.lst 파일에 stanza가 있다고 가정해 보자(리스트 2 참조).


리스트 2. Fedora 8을 부팅하는 일반적인 GRUB stanza
                           
title Fedora (2.6.26.8-57.fc8)
        root (hd0,5)
        kernel /boot/vmlinuz-2.6.26.8-57.fc8 ro root=LABEL=FEDORA8 rhgb quiet
        initrd /boot/initrd-2.6.26.8-57.fc8.img

이 시스템을 실행 레벨 3으로 실행하려면 부트 항목이 표시될 때까지 기다린 후 이 항목을 선택하고 'e'를 입력하여 항목을 편집한다. GRUB 옵션에 따라 키를 눌러서 부트 항목을 표시하고 'p'와 비밀번호를 입력하여 편집 잠금을 해제해야 하는 경우도 있다. Fedora 8 시스템의 GRUB 화면은 그림 1과 같다.


그림 1. GRUB에서 부트 항목 선택하기
GRUB에서 부트 항목 선택하기 

이 예제의 경우에는 이제 root, kernel 및 initrd 행이 표시되어야 한다. 커서를 "kernel"로 시작하는 행으로 이동하고 'e'를 눌러서 행을 편집한다. 이제 Fedora 8 시스템의 GRUB 화면이 그림 2와 같이 표시된다.


그림 2. 편집할 커널 항목 선택하기
편집할 커널 항목 선택하기 

마지막으로 행 끝으로 커서를 이동하고 공백과 '3'을 추가한다. 원하는 경우 'quiet'를 제거할 수 있으며, 필요에 따라 다른 매개변수를 수정할 수도 있다. 이제 Fedora 8 시스템의 GRUB 화면이 그림 2과 같이 표시된다.


그림 3. 시작 실행 레벨을 3으로 설정하기
시작 실행 레벨을 3으로 설정하기 

마지막으로 Enter를 눌러 변경사항을 저장한 다음 'b'를 입력하여 시스템을 부팅한다.

참고: LILO 또는 GRUB2를 사용하여 이 작업을 수행하는 단계는 GRUB을 사용할 때와 다르지만 커널의 시작 방법을 편집한다는 기본 원칙은 같다. 다른 시스템 또는 다른 배포판의 GRUB 화면은 이 기사에서 소개하는 화면과 많이 다를 수 있다. 일반적으로 필요한 정보를 제공하는 프롬프트가 표시된다.

실행 레벨 3에서 설정 작업을 마친 후에는 아마도 실행 레벨 5로 전환하고 싶을 것이다. 다행히도 시스템을 다시 부팅하지 않아도 된다. telinit 명령을 사용하여 다른 실행 레벨로 전환할 수 있다. runlevel 명령을 사용하여 이전 실행 레벨과 현재 실행 레벨을 표시한다. 첫 번째 출력 문자가 'N'이면 시스템이 부팅된 이후 실행 레벨이 변경되지 않았다는 의미이다. 리스트 3에서는 실행 레벨을 확인하고 변경하는 방법을 보여 준다.


리스트 3. 목록 3. 실행 레벨을 확인하고 변경하기
                    
[root@pinguino ~]# runlevel
N 3
[root@pinguino ~]# telinit 5

telinit 5를 입력하면 여러 메시지가 빠르게 표시된 후 구성된 그래픽 로그인 화면으로 전환된다. 터미널 창을 열고 실행 레벨이 변경되었는지 확인한다(리스트 4 참조).


리스트 4. 새 실행 레벨 확인하기
                    
[root@pinguino ~]# runlevel
3 5

ls 명령을 사용하여 telinit 명령의 긴 목록을 표시해 보면 이 명령이 실제로는 init 명령의 기호 링크라는 것을 알 수 있다. 리스트 5에서 이를 보여 준다.


리스트 5. init에 대한 기호 링크인 telinit
                    
[root@pinguino ~]# ls -l $(which telinit)
lrwxrwxrwx 1 root root 4 2008-04-01 07:50 /sbin/telinit -> init

init 실행 파일은 자신이 init으로 호출되었는지 아니면 telinit으로 호출되었는지 여부와 그에 따른 작동을 알고 있다. init은 부트 시간에 PID 1로 실행되기 때문에 나중에 telinit이 아니라 init을 사용하여 호출하는 경우도 충분히 인식할 수 있다. 이 경우 init 실행 파일은 사용자가 telinit을 호출한 것처럼 작동하기를 바란다고 가정한다. 예를 들어, telinit 5 대신 init 5를 사용하여 실행 레벨 5로 전환할 수 있다.

단일 사용자 모드

DOS나 Windows 같은 개인용 컴퓨터 운영 체제와는 달리 Linux는 본질적으로 다중 사용자 시스템이다. 하지만 이 특성이 문제가 되는 경우도 있다. 예를 들어, 주 파일 시스템이나 데이터베이스를 복구해야 하거나 새 하드웨어를 설치하고 테스트해야 하는 경우가 있다. 이러한 상황에 효과적으로 대처하는 방법은 실행 레벨 1 또는 단일 사용자 모드이다. 실제 구현은 배포판에 따라 달라지지만 일반적으로 최소 시스템만을 사용하는 쉘에서 시작하게 된다. 대개 네트워크 기능이 포함되지 않으며 실행 중인 디먼도 거의 없거나 아예 없다. 일부 시스템에서는 로그인을 통한 인증이 필요하지만 쉘 프롬프트에서 루트로 바로 실행되는 시스템도 있다. 단일 사용자 모드가 구원자가 될 수도 있지만 시스템을 파괴할 수도 있으므로 루트 권한으로 실행할 때는 언제나 주의를 기울여야 한다. 작업을 완료한 후에는 바로 정상 다중 사용자 모드로 다시 부팅한다.

일반 다중 사용자 실행 레벨로 전환할 때와 마찬가지로 telinit 1을 사용하여 단일 사용자 모드로 전환할 수 있다. 표 1에서 언급한 것처럼 's'와 'S'가 실행 레벨 1에 대한 별명이므로 telinit s를 대신 사용할 수도 있다.

정상 종료

telinit 또는 init을 사용하여 다중 사용자 활동을 중지하고 단일 사용자 모드로 전환할 수 있기는 하지만 이 경우 시스템이 갑자기 전환되기 때문에 사용자의 작업이 손실되고 프로세스가 비정상적으로 종료될 수 있다. 시스템을 종료하거나 다시 부팅할 때는 주로shutdown 명령이 사용된다. 이 명령은 먼저 로그인된 모든 사용자에게 경고 메시지를 보내고 이후 로그인을 차단한다. 그런 다음init에게 실행 레벨을 전환하도록 신호를 보낸다. 그러면 init 프로세스가 실행 중인 모든 프로세스에 SIGTERM 신호를 보내서 데이터를 저장하거나 정상적으로 종료할 수 있는 기회를 제공한다. 5초 또는 다른 지연 시간(지정된 경우)이 경과된 후 init이 SIGKILL 신호를 보내서 남아 있는 각 프로세스를 강제로 종료한다.

기본적으로 shutdown은 실행 레벨 1(단일 사용자 모드)로 전환된다. -h 옵션을 사용하여 시스템을 정지하거나 -r 옵션을 사용하여 다시 부팅할 수 있다. 사용자가 지정한 메시지와 함께 표준 메시지가 발행된다. hh:mm 형식의 절대 시간이나 n 형식의 상대 시간으로 시간을 지정할 수 있으며 여기서, n은 종료될 때까지의 분 수이다. 즉각적인 종료의 경우에는 now를 사용한다. 이는 +0에 해당한다.

지연된 종료를 실행한 후 시간이 아직 만료되지 않은 경우 명령이 포그라운드에서 실행 중이면 에는 Ctrl-c를 눌러서 종료를 취소하거나 shutdown-c 옵션과 함께 실행하여 보류 중인 종료를 취소할 수 있다. 리스트 6에서는 shutdown을 사용하고 종료하는 방법을 보여 주는 여러 예제를 보여 준다.


리스트 6. 종료 예제
                    
[root@pinguino ~]# shutdown 5 File system recovery needed

Broadcast message from root (pts/1) (Tue Jan  4 08:05:24 2011):

File system recovery needed
The system is going DOWN to maintenance mode in 5 minutes!
^C
Shutdown cancelled.
[root@pinguino ~]# shutdown -r 10 Reloading updated kernel&
[1] 18784
[root@pinguino ~]#
Broadcast message from root (pts/1) (Tue Jan  4 08:05:53 2011):

Reloading updated kernel
The system is going DOWN for reboot in 10 minutes!

[root@pinguino ~]# fg
shutdown -r 10 Reloading updated kernel
^C
Shutdown cancelled.
[root@pinguino ~]# shutdown -h 23:59&
[1] 18788
[root@pinguino ~]# shutdown -c

Shutdown cancelled.
[1]+  Done                    shutdown -h 23:59

마지막 예제의 경우 경고 메시지가 전송되지 않는다. 종료될 때까지의 시간이 15분을 초과하는 경우에는 이벤트 15분 전까지 메시지가 전송되지 않는다(리스트 7 참조). 또한 리스트 7에서는 -t 옵션을 사용하여 SIGTERM 및 SIGKILL 신호 사이의 기본 지연 시간을 5초에서 60초로 늘리는 방법도 보여 준다.


리스트 7. 또 다른 종료 예제
                    
[root@pinguino ~]# date;shutdown -t60 17 Time to do backups&
Tue Jan  4 08:12:55 EST 2011
[1] 18825
[root@pinguino ~]# date
Tue Jan  4 08:14:13 EST 2011
[root@pinguino ~]#
Broadcast message from root (pts/1) (Tue Jan  4 08:14:55 2011):

Time to do backups
The system is going DOWN to maintenance mode in 15 minutes!

종료를 취소한 경우에는 wall 명령을 사용하여 모든 사용자에게 시스템이 종료되지 않을 것이라는 사실을 알리는 경고를 전송해야 한다.

앞에서 언급한 것처럼 telinit(또는 init)을 사용하여 시스템을 종료하거나 다시 부팅할 수 있다. telinit을 사용했던 다른 경우와 마찬가지로 이번에도 사용자에게 경고가 전송되지 않는다. 그리고 이 명령은 SIGTERM 및 SIGKILL 신호 사이의 지연 시간이 여전히 남아 있더라도 즉시 적용된다. telinit, init 및 shutdown의 추가 옵션에 대한 정보는 해당 매뉴얼 페이지를 참조한다.

halt, reboot 및 poweroff

종료 및 다시 부팅과 관련된 몇 가지 명령을 더 알고 있어야 한다.

  • halt 명령은 시스템을 정지시킨다.
  • poweroff 명령은 halt 명령에 대한 기호 링크이며, 시스템을 정지시킨 후 전원을 끄려고 시도한다.
  • reboot 명령도 halt 명령에 대한 기호 링크이며, 시스템을 정지시킨 후 다시 부팅한다.

시스템의 실행 레벨이 0 또는 6이 아닐 때 이러한 명령을 실행할 경우에는 해당 shutdown 명령이 대신 호출된다.

이러한 명령과 함께 사용할 수 있는 추가 옵션과 이러한 옵션의 조작에 대한 자세한 정보는 매뉴얼 페이지를 참조한다.

/etc/inittab

지금까지 일부 시스템에서 Ctrl-Alt-Delete를 누르면 다시 부팅이 되는 이유나 이 실행 레벨을 구성하는 방법이 궁금했을 것이다. /etc/inittab에 있는 id 필드가 기억나는가? /etc/inittab에는 다른 필드도 여러 개 있으며 rc1.d나 rc5.d와 같은 디렉토리에 init 스크립트 세트도 있다. 여기서, 숫자는 해당 디렉토리의 스크립트가 적용되는 실행 레벨을 식별한다. 리스트 8에서는 Fedora 8 시스템의 전체 inittab을 보여 준다.


리스트 8. Fedora 8의 전체 inittab
#
# inittab       This file describes how the INIT process should set up
#               the system in a certain run-level.
#
# Author:       Miquel van Smoorenburg, <miquels@drinkel.nl.mugnet.org>
#               Modified for RHS Linux by Marc Ewing and Donnie Barnes
#

# Default runlevel. The runlevels used by RHS are:
#   0 - halt (Do NOT set initdefault to this)
#   1 - Single user mode
#   2 - Multiuser, without NFS (The same as 3, if you do not have networking)
#   3 - Full multiuser mode
#   4 - unused
#   5 - X11
#   6 - reboot (Do NOT set initdefault to this)
#
id:5:initdefault:

# System initialization.
si::sysinit:/etc/rc.d/rc.sysinit

l0:0:wait:/etc/rc.d/rc 0
l1:1:wait:/etc/rc.d/rc 1
l2:2:wait:/etc/rc.d/rc 2
l3:3:wait:/etc/rc.d/rc 3
l4:4:wait:/etc/rc.d/rc 4
l5:5:wait:/etc/rc.d/rc 5
l6:6:wait:/etc/rc.d/rc 6

# Trap CTRL-ALT-DELETE
ca::ctrlaltdel:/sbin/shutdown -t3 -r now

# When our UPS tells us power has failed, assume we have a few minutes
# of power left.  Schedule a shutdown for 2 minutes from now.
# This does, of course, assume you have powerd installed and your
# UPS connected and working correctly.
pf::powerfail:/sbin/shutdown -f -h +2 "Power Failure; System Shutting Down"

# If power was restored before the shutdown kicked in, cancel it.
pr:12345:powerokwait:/sbin/shutdown -c "Power Restored; Shutdown Cancelled"


# Run gettys in standard runlevels
1:2345:respawn:/sbin/mingetty tty1
2:2345:respawn:/sbin/mingetty tty2
3:2345:respawn:/sbin/mingetty tty3
4:2345:respawn:/sbin/mingetty tty4
5:2345:respawn:/sbin/mingetty tty5
6:2345:respawn:/sbin/mingetty tty6

# Run xdm in runlevel 5
x:5:respawn:/etc/X11/prefdm -nodaemon

대개 # 기호로 시작하는 행은 주석이다. 다른 행에는 다음과 같은 형식의 여러 필드가 있다.
id:runlevels:action:process

id
최대 4개의 문자로 구성된 고유 ID이다. 기존 버전에서는 두 개의 문자로 제한되어 있었기 때문에 두 개의 문자만 사용되는 경우를 자주 볼 수 있다.
runlevels
이 ID의 조치를 수행할 실행 레벨을 나열한다. 실행 레벨이 나열되지 않은 경우에는 모든 실행 레벨에서 조치를 수행한다.
action
수행할 여러 가능한 조치를 설명한다.
process
이 행의 조치를 수행할 때 실행해야 하는 프로세스를 지정한다(해당하는 경우).

표 3에서는 /etc/inittab에서 지정할 수 있는 몇 가지 일반적인 조치를 보여 준다. 다른 가능한 조치에 대한 정보는 inittab의 매뉴얼 페이지를 참조한다.


표 3. 몇 가지 일반적인 inittab 조치
조치용도
respawn프로세스가 종료될 때마다 프로세스를 다시 시작한다. 일반적으로 로그인을 모니터링하는 getty 프로세스에 사용된다.
wait지정된 실행 레벨이 실행될 때 프로세스를 한 번 시작한 후 프로세스가 종료될 때까지 기다렸다가 init을 진행한다.
once지정된 실행 레벨이 실행되면 프로세스를 한 번 시작한다.
initdefault시스템 부팅 후 실행할 실행 레벨을 지정한다.
ctrlaltdelinit이 SIGINT 신호를 받을 때 예를 들어, 어떤 사용자가 시스템 콘솔에서 CTRL-ALT-DEL을 눌렀을 때 연관된 프로세스를 실행한다.

리스트 9에서는 리스트 8의 Ctrl-Alt-Delete에 대한 항목만 보여 준다. 이제 Ctrl-Alt-Delete를 누를 때 시스템이 다시 부팅되는 이유를 알 수 있다.


리스트 9. Ctrl-Alt-Delete 트랩핑
                    
# Trap CTRL-ALT-DELETE
ca::ctrlaltdel:/sbin/shutdown -t3 -r now

초기화 스크립트

리스트 8을 보면 다음과 같은 행이 여러 개 있다.

l5:5:wait:/etc/rc.d/rc 5

이 예제에서는 실행 레벨 5가 시작될 때마다 init이 매개변수 5를 사용하여 /etc/rc.d/rc 스크립트(또는 명령)를 실행한다. init은 이 명령이 완료될 때까지 기다린 후 다른 작업을 수행한다.

시스템을 시작하거나, 실행 레벨을 변경하거나, 종료할 때 init에 사용되는 이러한 스크립트는 일반적으로 /etc/init.d 또는 /etc/rc.d 디렉토리에 저장된다. 각 실행 레벨 n별로 하나의 디렉토리가 있는 rcn.d 디렉토리에는 일련의 기호 링크가 있으며, 이러한 기호 링크는 스크립트가 실행 레벨을 시작할 때 실행되는지 또는 실행 레벨을 종료할 때 중지되는지 여부를 제어한다. 이러한 링크는 K 또는 S로 시작하며, 그 뒤에 두 자리 숫자와 서비스의 이름이 있다(리스트 10 참조).


리스트 10. Init 스크립트
                    
[root@pinguino ~]# find /etc -path "*rc[0-9]*.d/???au*"
/etc/rc.d/rc2.d/S27auditd
/etc/rc.d/rc2.d/K72autofs
/etc/rc.d/rc4.d/S27auditd
/etc/rc.d/rc4.d/S28autofs
/etc/rc.d/rc5.d/S27auditd
/etc/rc.d/rc5.d/S28autofs
/etc/rc.d/rc0.d/K72autofs
/etc/rc.d/rc0.d/K73auditd
/etc/rc.d/rc6.d/K72autofs
/etc/rc.d/rc6.d/K73auditd
/etc/rc.d/rc1.d/K72autofs
/etc/rc.d/rc1.d/K73auditd
/etc/rc.d/rc3.d/S27auditd
/etc/rc.d/rc3.d/S28autofs
[root@pinguino ~]# cd /etc/rc.d/rc5.d
[root@pinguino rc5.d]# ls -l ???a*
lrwxrwxrwx 1 root root 16 2008-04-07 11:29 S27auditd -> ../init.d/auditd
lrwxrwxrwx 1 root root 16 2008-04-01 07:51 S28autofs -> ../init.d/autofs
lrwxrwxrwx 1 root root 15 2008-04-01 14:03 S44acpid -> ../init.d/acpid
lrwxrwxrwx 1 root root 13 2008-04-01 07:50 S95atd -> ../init.d/atd
lrwxrwxrwx 1 root root 22 2008-04-01 07:54 S96avahi-daemon -> ../init.d/avahi-daemon
lrwxrwxrwx 1 root root 17 2008-11-17 13:40 S99anacron -> ../init.d/anacron

이 목록을 보면 audit 및 autofs 서비스의 모든 실행 레벨에 Knn 항목이 있고, 실행 레벨 3 및 5에 Snn 항목이 있다. S는 해당 실행 레벨이 실행될 때 서비스가 시작된다는 것을 나타내는 반면 K 항목은 중지되어야 한다는 것을 나타낸다. 링크 이름의 nn 부분은 서비스를 시작 또는 중지할 때 적용되는 우선순위를 나타낸다. 이 예제에서 auditautofs보다 먼저 시작되고 나중에 중지된다.

자세한 정보는 init 및 inittab의 매뉴얼 페이지를 참조한다.

Init과 Upstart 비교

이 기사에서 살펴본 것처럼 Linux 시스템의 기존 부팅 방법은 UNIX System V init 프로세스를 기반으로 하고 있다. 먼저 초기 RAM 디스크(initrd)를 로드한 다음 init이라는 프로그램에 제어를 전달하며, 이 프로그램은 일반적으로 sysvinit 패키지의 일부로 설치된다. 그런 다음 init 프로그램이 일련의 스크립트를 미리 정의된 순서에 따라 실행하여 시스템을 시작한다. 예상했던 자원을 사용할 수 없는 경우 일반적으로 init 프로세스는 해당 자원을 사용할 수 있을 때까지 기다린다. 시스템이 시작할 때 모든 자원이 인식 및 연결되는 시스템의 경우에는 이 작업이 정상적으로 작동하지만 시작 시에 사용 가능하지 않을 수도 있는 핫플러그 디바이스, 네트워크 파일 시스템 및 네트워크 인터페이스를 지원하는 최신 시스템의 경우에는 새로운 도전 과제가 발생한다. 장시간 혹은 비교적 긴 시간 동안이라도 사용 가능하지 않을 수도 있는 하드웨어를 기다린다는 것은 분명 원하는 바가 아니다.

이에 대한 대안으로 upstart라는 초기화 프로세스가 2006년에 Ubuntu 6.10("Edgy Eft")에서 처음 소개되었다. 이제는 이 프로세스가 Ubuntu 및 Fedora의 init을 대체했으며 무엇보다도 init의 잔재가 남아 있기는 하지만 한동안은 upstart의 진가가 충분히 실현되지 않을 것이다.

이전 시스템에 사용되는 정적인 init 스크립트 세트와는 대조적으로 upstart 시스템은 이벤트에 의해 구동된다. 이벤트는 하드웨어 변경, 태스크 시작 또는 중지, 시스템의 기타 프로세스에 의해 트리거될 수 있다. 이벤트는 통틀어서 작업이라고 불리는 태스크 또는 서비스를 트리거하는 데 사용된다. 예를 들어, USB 드라이브를 연결하면 udev 서비스에 의해 block-device-added 이벤트가 발생하면서 정의된 태스크가 실행되어 /etc/fstab을 검사하고 드라이브를 마운트한다(해당하는 경우). 또 다른 예로, Apache 웹 서버는 네트워크 및 필수 파일 시스템 자원을 모두 사용할 수 있는 경우에만 시작될 수 있다.

Upstart 초기화 프로그램은 /sbin/init을 대체한다. Upstart 작업은 /etc/init 디렉토리 및 서브디렉토리에 정의되어 있다. Upstart 시스템은 현재 /etc/inittab 및 System V init 스크립트를 처리한다. 최신 Fedora 릴리스와 같은 시스템의 경우에는 /etc/inittab에 initdefault의 ID 항목만 들어 있다. 최신 Ubuntu 시스템에는 기본적으로 /etc/inittab이 없지만 기본 실행 레벨을 지정하려는 경우 사용자가 /etc/inittab을 작성할 수 있다.

또한 upstart에는 upstart init 디먼과의 상호 작용을 지원하는 initctl 명령이 있다. 이 명령을 사용하면 작업을 시작, 중지 또는 나열하고, 작업 상태를 확인하고, 이벤트를 발생시키고, init 프로세스를 다시 시작하는 등의 작업을 수행할 수 있다. 리스트 11에서는 initctl을 사용하여 Fedora 13 시스템의 upstart 작업 목록을 가져오는 방법을 보여 준다.


리스트 11. initctl을 사용하여 upstart init 디먼과 상호 작용하기
[ian@echidna ~]$ initctl list
rc stop/waiting
tty (/dev/tty3) start/running, process 1486
tty (/dev/tty2) start/running, process 1484
tty (/dev/tty6) start/running, process 1492
tty (/dev/tty5) start/running, process 1490
tty (/dev/tty4) start/running, process 1488
plymouth-shutdown stop/waiting
control-alt-delete stop/waiting
system-setup-keyboard start/running, process 1000
readahead-collector stop/waiting
vpnc-cleanup stop/waiting
quit-plymouth stop/waiting
rcS stop/waiting
prefdm start/running, process 1479
init-system-dbus stop/waiting
ck-log-system-restart stop/waiting
readahead stop/waiting
ck-log-system-start stop/waiting
start-ttys stop/waiting
readahead-disable-services stop/waiting
ck-log-system-stop stop/waiting
rcS-sulogin stop/waiting
serial stop/waiting

Upstart에 대한 자세한 정보는 참고자료를 참조한다.

이제 Linux 실행 레벨, 종료 및 다시 부팅에 대한 소개를 마친다.


참고자료

교육

  • developerWorks의 LPIC-1 로드맵을 사용하여 2009년 4월 목표를 기반으로 하는 LPIC-1 인증을 준비하는 데 도움을 줄 수 있는 developerWorks 기사를 찾을 수 있다.
  • LPIC Program 사이트에서 Linux Professional Institute의 Linux 시스템 관리 인증의 세 가지 레벨에 대한 자세한 목표, 작업 목록 및 샘플 질문을 찾을 수 있다. 특히, LPI exam 101LPI exam 102의 2009년 4월 목표를 확인하자. 항상 LPIC Program 사이트를 참조하여 최신 목표를 확인한다.
  • Linux 기초를 학습하고 2009년 4월 이전의 LPI 시험 목표를 바탕으로 한 시스템 관리자 인증 시험을 준비하려면 developerWorks에서 전체 LPI exam prep series를 검토한다.
  • Linux BootPrompt-HowTo는 부트 매개변수를 이해하는 데 도움이 된다.
  • Upstart overview에서 upstart에 대한 자세한 정보를 볼 수 있다.
  • Linux에서는 수백 개의 기술자료 목록과 함께, Linux 개발자와 관리자를 위한 다양한 다운로드, 토론 포럼 및 다른 참고자료를 찾을 수 있다.
  • developerWorks 기술 행사 및 웹 캐스트를 통해 다양한 IBM 제품 및 IT 산업 주제에 대한 최신 정보를 얻을 수 있다.
  • 무료 developerWorks Live! briefing을 통해 최신 IBM 제품 및 도구에 대한 정보뿐만 아니라 IT 업계의 최신 경향까지도 빠르게 확인할 수 있다.
  • developerWorks on-demand demos에서는 입문자를 위한 제품 설치 및 설정부터 숙련된 개발자를 위한 고급 기능까지 망라된 다양한 데모를 제공한다.
  • Twitter의 developerWorks를 팔로우(follow)하거나 developerWorks에 대한 Linux 트윗(tweet)의 피드를 구독하자.

제품 및 기술 얻기

  • 자신에게 가장한 적합한 방법으로 IBM 제품을 평가해 보자. 시험판 제품을 다운로드하거나, 온라인으로 제품을 사용해 보거나, 클라우드 환경에서 제품을 사용하거나, SOA Sandbox에서 SOA(Service Oriented Architecture)를 효과적으로 구현하는 방법을 배울 수 있다.

토론

크리에이티브 커먼즈 라이센스
Creative Commons License
2012/05/18 09:44 2012/05/18 09:44
받은 트랙백이 없고, 댓글이 없습니다.

댓글+트랙백 RSS :: http://blog.hwport.com/rss/response/661

댓글+트랙백 ATOM :: http://blog.hwport.com/atom/response/661


본 내용은 "http://www.ibm.com/developerworks/kr/library/l-fs6/index.html" 에서 발췌한 것임을 밝힙니다.



init wrapper

시작

Part 5에서, init wrapper의 개념을 설명했다. 그리고 다양한 devfs 초기화 문제를 해결하는 데 있어서 적격인 이유도 설명했다. init wrapper를 단계별로 살펴보고 각각 무엇을 수행하는지 살펴보자:


init wrapper-윗 부분
#!/bin/bash
# Copyright 2001 Daniel Robbins <drobbins@gentoo.org>, Gentoo Technologies, Inc.
# Distributed under the GNU General Public License, version 2.0 or later.

trap ":" INT QUIT TSTP	
export PATH=/sbin:/bin:/usr/sbin:/usr/bin
umask 022

if [ $$ -ne 1 ]
then
	exec /sbin/init.system $*
fi

init wrapper는 bash 스크립트이다. 스크립트의 상단에 #!/bin/bash 가 있기 때문이다. init wrapper가 실행하기 위해서는 bash 2.0 또는 이후 버전이 필요하다; /bin/bash --version 을 타이핑하여 bash 쉘이 최신 버전인지 확인한다. 최신 버전이 아니라면 /bin/bash2실행파일을 설치해야한다. 최신 버전이라면 #!/bin/bash2를 읽을 수 있도록 스크립트의 첫 번째 라인을 변경한다.

이제, 스크립트를 보자. trap 명령어는 스크립트가 실행되는 동안 사용자에 의해 스크립트가 인터럽트 되지 않도록 한다 (예를 들어, 부팅 시 control-C를 누른다). 그런 다음, 적절한 디폴트 경로를 export 하고 디폴트 umask를 022로 설정한다. 부팅 프로세스 동안 가능한 한 빨리 디폴트 umask를 설정하는 것이 좋다. 왜냐하면 2.4 kernel 이전 버전들은 디폴트 umask가 0이 되는 버그를 가지고 있었다. 심각한 보안 위협을 초래할 수 있다.

다음에, 첫 번째 조건문인 if [ $$ -ne 1 ]. bash$$를 현재 실행되는 프로세스의 process ID까지 확장한 것을 볼 수 있다. 이는 "process ID가 1인가?" 를 묻고 있는 것이다. 이것이 중요한 이유는 부팅 프로세스 동안 커널에 의해 시작한다면 PID 1을 갖게 된다. PID 1은 init 프로세스 동안 보존된다. PID 1이 아니라면, 시스템이 이미 부팅된 후에 명령행에서 실행하고 있다는 것을 알게된다. 일반적인 일은 아니다. 왜냐하면 /sbin/init 명령어는 수퍼유저가 이미 부팅된 시스템의 runlevel을 변경할 수 있도록 하는 이중의 목적을 가지고 있기 때문이다. 이와 같은 경우, 원래의 /sbin/initexec 하여 /sbin/init.system으로 이름을 변경한다. $* 변수를 사용하여 명령행 인자를 init.system 에 전달하면 init wrapper는 종료되고 init.system 은 실행된다.

커널 부팅 옵션

하지만, wrapper가 부팅 시간 동안 커널에 의해 시작된다면 bash의 PID는 1이 되고 bash가 계속하여 wrapper를 실행할 때 이러한 조건은 건너뛰게 된다. 다음 예제를 보자:


init wrapper-중간 부분
mount -n /proc
devfs="yes"
for copt in `cat /proc/cmdline`
do
	if [ "${copt%=*}" = "wrapper" ]
	then
		parms=${copt##*=}
		#parse wrapper option
		if [ "${parms/nodevfs//}" != "${parms}" ]
		then
			devfs="no"
		fi
	fi
done

코드가 여기까지 진행되었다는 것은 부팅 프로세스 동안 커널에 의해 실행하고 있다는 의미가 된다; /proc을 root 파일시스템에 마운트한다. 이것은 현재 읽기 전용이다. 그런 다음 bash 코드를 실행한다. 커널을 통해서 우리는 어떤 옵션이 LILO 또는 GRUB을 통해 이것으로 전달하는지를 볼 수 있다. /proc/cmdline의 내용을 참고하면 된다. /proc/cmdline 내용은 다음과 같다:


/proc/cmdline의 내용
# cat /proc/cmdline
root=/dev/hda6 hda=89355,16,63 mem=524224K

/proc/cmdline를 이용한다. wrapper라고 하는 커널 부팅 변수를 위해 이것을 조사하면 된다. wrapper=nodevfs 커널 부팅 옵션 사이에 나타난다면 스크립트는 devfs를 실행할 수 없다는 것을 알게된다. 하지만, 변수가 /proc/cmdline에 나타나지 않는다면, wrapper는 devfs 초기화를 진행할 것이다. 이것으로 알 수 있는 것은 wrapper=nodevfs 커널 부팅 옵션으로 부팅하면 devfs를 쉽게 실행불가로 만든다는 것이다. 만일 그렇게 되면, devfs 변수는 no가 되고 반대의 경우 yes가 된다.

끝내기

다음은 wrapper의 나머지 부분이다:


init wrapper의 나머지 부분
if [ "$devfs" = "yes" ]
then
 if [ -e /dev/.devfsd ] 
 then
	clear
	echo
	echo "The init wrapper has detected that /dev has been automatically mounted by"
	echo "the kernel. This will prevent devfs from automatically saving and"
	echo "restoring device permissions. While not optimal, your system will still"
	echo "be able to boot, but any perm/ownership changes or creation of new compat."
	echo "device nodes will not be persistent across reboots until you fix this"
	echo "problem."
	echo
	echo "Fortunately, the fix for this problem is quite simple; all you need to"
	echo "do is pass the \"devfs=nomount\" boot option to the kernel (via GRUB"
	echo "or LILO) the next time you boot.  Then /dev will not be auto-mounted."
	echo "The next time you compile your kernel, be sure that you do not"
	echo "enable the \"Automatically mount filesystem at boot\" devfs kernel"
	echo "configuration option.  Then the \"devfs=nomount\" hack will no longer be"
	echo "needed."
	echo
     read -t 15 -p "(hit Enter to continue or wait 15 seconds...)" 
 else	
	mount -n /dev /dev-state -o bind
	mount -n -t devfs none /dev
	if [ -d /dev-state/compat ]
	then
			echo Copying devices from /dev-state/compat to /dev
			cp -ax /dev-state/compat/* /dev
	fi
 fi
 /sbin/devfsd /dev >/dev/null 2>&1;
fi 

exec /sbin/init.system $*

devfsyes가 될 경우에만 실행되는 조건문을 보고있다. 이것이 그러한 경우가 아니라면, devfs 초기화는 완전히 건너 뛰게 되고 devfs는 마운트되지 않는다. 이것이 전통적인 non-devfs 부팅이다.

하지만 devfs를 설정하고 있다면 조건문을 수행해야 한다. devfs가 커널에 의해 이미 설치되어 있는지를 확인한다; /dev/.devfsd 캐릭터 디바이스가 존재하는 지를 확인하는 것이다. devfs가 마운트되면 이 디바이스는 자동으로 커널에 의해 만들어진다. 그리고 앞으로의 devfsd 프로세스는 커널과 통신할 때 사용하게 될 것이다. devfs가 이미 마운트되었다면 (사용자가 "Automatically mount devfs at boot" 커널 옵션을 선택했기 때문에), 정보 메시지를 출력하여 사용자로 하여금 우리가 devfs의 영속 기능을 설정할 수 없게 된다는 것을 알 수 있도록 한다. devfs는 커널에 의해 마운트 되지 않았기 때문에 우리는 단지 그것만이 .

디바이스 영속성

모든 것이 정상이라면 devfs 설치를 시작한다: /dev가 /dev-state으로 바인드 마운트(bind-mount)되고 devfs 파일시스템은 /dev에 마운트 된다. 그런 다음, /dev-state/compat 디렉토리의 존재를 확인하고 반복적으로 그 내용을 /dev에 복사한다. 이러한 절차가 처음에는 불필요한 반복작업으로 보일 수도 있지만 필수적이고도 유용하다. 이유는 우리가 필요로 하는 compat 디렉토리는 devfsd의 영속 기능이 devfs-enabled 드라이버와 함께 작동할 수 있기 때문이다.

non-devfs 커널 모듈을 사용하게 된다면, /dev에 수동으로 디바이스 노드를 만들어야 한다. 이러한 접근방식의 문제점은 이 새로운 디바이스 노드가 devfsd에 의해 무시되어서 다음에 재부팅 할 때 사라지게 된다. 솔루션으로서는 /dev-state/compat 디렉토리를 갖는 것이다; non-devfs 모듈을 가지고 있다면, /dev-state/compat에 구식의 디바이스 노드를 만들면 그들은 부팅 시 수동으로 devfs 파일시스템에 추가 될 것이다. init wrapper 덕택이다.

마지막으로 devfsd를 시작하고 조건문을 종료하고 실제 init인, /sbin/init.systemexec 하여 표준 시스템 부팅 프로세스를 시작한다.

Init wrapper 설치

init wrapper를 설치하는 방법이다. 우선, wrapper.sh용 소스를 그랩(grab)하고, 시스템에 저장한다. 그리고 나서, 다음과 같이 한다:


init wrapper 설치
# cd /sbin
# cp init init.system
# cp /path/to/wrapper.sh init
# chmod +x init

init wrapper가 설치되었다.

umount tweaking

init wrapper를 사용함으로서, 매우 복잡한 "initscript tweaking"을 피했다. 그럼에도 불구하고, 한 가지의 조정해야 할 것이 있다. rc 스크립트는 devfs가 마운트 된 /dev를 가지고 있는 root 파일시스템을 언마운트 하는데에 어려움을 겪을 것이다. 다행히, 간단한 픽스를 적용할 수 있다. rc 스크립트 디렉토리를 grep 한다. umount cd /etc/rc.d; grep -r umount * 또는 cd /etc/init.d; grep -r umount * 를 타이핑한다 (rc 스크립트가 설치된 배포판에 따라 다름). 그런 다음, umount를 참조하는 모든 스크립트에서 이것이 -r 옵션과 호출되는지를 확인한다. 가장 중요한 것은 root 파일시스템을 언마운트 하는 특정 umount 명령어이다.

-r 옵션은 umount에게 언마운트가 되지 않을 경우 읽기 전용으로 파일시스템을 리마운트 하도록 명령한다. 이것은 root 파일시스템을 영속 상태에 놓기에 충분하며 재부팅 준비가 되도록 한다.

이제, 재부팅 할 준비가 되었다; 그 전에, devfsd를 검토하여 /etc/devfsd.conf 를 진행시켜 호환 디바이스와 디바이스 영속을 가능토록 한다.

devfsd.conf

/etc/devfsd.conf를 편집기에 로딩한다. devfsd.conf의 첫 4줄이다:


devfsd.conf-윗 부분
REGISTER        .*              MKOLDCOMPAT
UNREGISTER      .*              RMOLDCOMPAT
REGISTER        .*              MKNEWCOMPAT
UNREGISTER      .*              RMNEWCOMPAT

위 4줄은 event (REGISTER 또는 UNREGISTER), 정규식 (.*), action (*COMPAT strings)으로 구성되어 있다. 첫 번째 줄은 디바이스가(.*는 모든 디바이스와 매치하는 정규식이다) 커널에 등록될 때 devfsd 에게 MKOLDCOMPAT action을 수행하도록 명령한다. MKOLDCOMPATaction은 devfsd 에 빌트인 되고 "devfs를 통해 등록된 디바이스와 상응하는 기존의 모든 호환 디바이스를 만든다"라는 의미로 해석된다. 설정을 진행시켜 나가면서, 디바이스에서 실행되는 RM*COMPAT action은 이러한 특별한 호환 디바이스를 마법처럼 사라지게 한다. 전체적으로 볼 때 위의 4 줄은 devfsd 에게 디바이스가 등록될 때 호환 디바이스를 만들도록 명령하고, 디바이스가 등록이 해지되었을 때 호환 디바이스를 제거하라고 명령하는 것이다. 이러한 명령어들 덕택에, IDE 디바이스 드라이버가/dev/ide/host0/bus0/target0/lun0/disc devfs 스타일의 디바이스를 시스템에 등록할 때, devfs는 자동적으로 /dev/hda 호환-스타일의 디바이스와 매칭(matching)시킨다. 이것은 구식의 디바이스 이름을 포함하고 있는 /etc/fstab을 읽는 mountfsck 같은 명령어에 매우 유용하다. 일반적으로 호환 디바이스를 만듦으로서 devfs로의 전환이 깔끔해진다. devfsd.conf의 다음 라인이다:

Module auto-loading


devfsd.conf
LOOKUP          .*              MODLOAD

이 엔트리는 devfsd 에게 모든 디바이스(.*)가 "검색"될 때마다 MODLOAD action을 실행하도록 명령한다. MODLOAD action은 modprobe /dev/mydev 가 실행되도록 한다. /dev/mydev는 특정 프로세스가 찾으려고 하는 디바이스의 이름이다. 이러한 기능으로 인해서 사운드 카드 드라이버가 music player를 시작할 때 자동으로 로딩될 수 있다.

디바이스 영속성

위 라인은 devfsd 에게 모든 디바이스 권한 또는 소유권 변경을 비롯하여 사용자가 만드는 새로운 호환 디바이스를 위한 리포지토리(repository)로서 /dev-state를 사용하도록 명령한다. 첫 번째 두 라인은 devfsd 에게 가상 터미널(pseudo-terminal) 디바이스가 커널에 등록되거나 속성이 변경될 때 어떤 특별한 action을 수행하지 않도록 명령하는 것이다. 이 라인이 없이 가상 터미널의 권한 및 소유권은 재부팅 시 보존된다.

다음 세 라인은 모든 다른 디바이스를 위해 /dev-state을 작동시킨다. 특별히, 디바이스가 등록되거나 devfsd 자체가 시작할 때 /dev-state로 부터 모든 속성을 복원할 것이다. 속성 변화와 새로 만들어진 호환 디바이스를 /dev-state에 백업한다.

CFUNCTION과 symlink

devfsd.conf를 끝마치기 위해서는 다음의 라인이 필요하다:


devfsd.conf, end
REGISTER        ^cdrom/cdrom0$          CFUNCTION GLOBAL symlink cdroms/cdrom0 cdrom
UNREGISTER      ^cdrom/cdrom0$          CFUNCTION GLOBAL unlink cdrom
REGISTER        ^misc/psaux$            CFUNCTION GLOBAL symlink misc/psaux mouse
UNREGISTER      ^misc/psaux$            CFUNCTION GLOBAL unlink mouse

마지막 4 라인은 선택적이다. 하지만 검토해 볼 가치는 있다. /dev-state 영속성이 디바이스 노드에 훌륭하게 작동되는 반면, symbolic link에는 전혀 영향을 미치지 않는다. 다시 말해서 무시된다. /dev/mouse 또는 /dev/cdrom symlink가 존재 할 뿐만 아니라 재부팅 시에도 영속성을 유지하는 방법이 궁금할 것이다. 다행히도 devfsd는 설정이 가능하며 이 4 라인은 트릭을 수행 할 것이다. 첫 번째 두 라인은 /dev/cdrom/cdrom0 디바이스가 등록될 때 devfsd 에게 명령하여 /dev/cdrom symlink가 나타나도록 한다. 이를 위해서, devfsd 는 실제로 여러분이 지정한 libc 함수로 동적 호출을 수행한다. 이 경우 symlink() 와 unlink()이다. 마지막 두 라인은 /dev/misc/psaux (PS/2 mouse) 디바이스가 devfs로 등록될 때 /dev/mouse symlink를 만들기 위해서 독자적인 접근 방식을 사용한다. 시스템에 이 라인을 커스터마이징하고 파일을 저장하라. devfsd.conf를 다운로드 할 수 있다.

재부팅 시 주의사항

재부팅 하기 전에, Richard Gooch의 devfs FAQ를 검토해보는 것도 좋을 것 같다; devfs naming scheme에 대한 정보는 새로운 방식의 디바이스 이름에 익숙해 지는 데에 유용할 것이다. (참고자료). 고급 파일시스템 개발자 가이드,Part 5 를 읽어보기 바란다. 부팅 관련 문제를 해결하는데에 도움이 된다. 새로운 init wrapper가 실패할 경우 emergency rescue instruction에 따라서 root 파일시스템을 읽기-쓰기로 리마운트 하고 다음 과정을 수행하여 제거할 수 있다:


pre-wrapper 상태로 리턴
# cd /sbin
# mv init wrapper.sh
# mv init.system init

파일시스템을 읽기 전용으로 리마운팅하고 재부팅하면 시스템은 pre-wrapper(wrapper 이전) 상태로 돌아온다.


참고자료

필자소개

Daniel Robbins는 Gentoo Technologies, inc.의 사장/CEO이다. 또 PC용 고급 Linux인 Gentoo Linux의 창설자이자, 차세대 Linux 포트 시스템인 Portage 시스템의 창시자이다. 또한 Macmillan사에서 출판하는 Caldera OpenLinux Unleashed, SuSE Linux Unleashed, Samba Unleashed에 집필활동을 하고 있다.

크리에이티브 커먼즈 라이센스
Creative Commons License
2012/05/18 09:29 2012/05/18 09:29
받은 트랙백이 없고, 댓글이 없습니다.

댓글+트랙백 RSS :: http://blog.hwport.com/rss/response/660

댓글+트랙백 ATOM :: http://blog.hwport.com/atom/response/660


본 내용은 "http://blog.foxb.kr/29" 에서 발췌한 것임을 밝힙니다.



usb 에 windows PE 를 ntfs 로 부팅하기

Windows PE 사용자 가이드를 살펴보면

부팅 가능한 Windows PE 디스크를 만들려면
  1. Windows PE CD(OEM 이외) 또는 Windows OPK CD(OEM)를 사용하여 대상 컴퓨터를 Windows PE로 시작합니다.
  2. 포맷된 활성 파티션을 만들어 레이블을 C로 지정합니다.
  3. 활성 파티션에서 C:\Minint 디렉터리를 만듭니다. 디렉터리 이름을 "Minint"로 지정해야 합니다.
  4. build_location\i386의 항목을 C:\Minint로 복사합니다.
    xcopy "C:\Build.x86\i386\*.*" C:\Minint /S
  5. build_location\i386에서 Ntdetect.com을 C 드라이브로 복사합니다.
    xcopy "C:\Build.x86\i386\ntdetect.com" C:\
  6. 대상 하드 디스크에서 C:\Minint\setupldr.bin을 C:\ntldr로 복사합니다.
    xcopy "C:\Minint\setupldr.bin" C:\ntldr
  7. 대상 컴퓨터를 다시 시작합니다.

    Windows PE를 사용하여 컴퓨터가 시작됩니다. Windows PE가 있는 파티션이 X 드라이브에 매핑됩니다.

파티션이 하나 있는 하드 디스크에 Windows PE를 저장한 다음 Windows 운영 체제를 사전 설치하면 Windows 운영 체제 디렉터리와 파일 및 Windows PE의 디렉터리와 파일을 포함하는 Minint 디렉터리는 모두 C 드라이브에 위치합니다.




이런 문구를 발견할수 있었습니다.

바로 ntfs로 포멧한 usb로 시험해 보았더니 ntldr 을 찾을수없다며 부팅이 안되는군요
그래서 저위에 6번 을 다시  살펴보니 저게 폴더가 아닌 파일이였습니다.
즉 setupldr.bin을 복사후 ntldr로 이름을 바꾸라는겁니다.

이 방법을 사용한다면 ntfs로 c:\ 드라이브 에서도 바로 가능할것 같습니다.
이로서 usb 에 windows PE 를 ntfs 로 부팅하기 성공!

usb를 ntfs로 포멧하는 유틸


크리에이티브 커먼즈 라이센스
Creative Commons License
2012/05/11 18:03 2012/05/11 18:03
받은 트랙백이 없고, 댓글이 없습니다.

댓글+트랙백 RSS :: http://blog.hwport.com/rss/response/659

댓글+트랙백 ATOM :: http://blog.hwport.com/atom/response/659


본 내용은 "http://dev.jpblog.co.kr/25" 에서 발췌한 것임을 밝힙니다.


----


삼바(SAMBA)?

 

(1) 개요: 마이크로소프트와 인텔은 윈도우시스템이 다른 시스템의 디스크나 프린터같은 자원을 공유할 수 있도록 Server Message Block(SMB)이라는 프로토콜을 개발하였는데, 삼바란 이 SMB를 이용하여 리눅스와 윈도우와의 자료 공유 및 하드웨어를 공유할 수 있도록 해준다. 현재 마이크로소프트에서는 SMB라는 이름보다는 좀 더 범위가 큰CIFS(Common Internet File System)라는 프로토콜로 부른다. 이 프로토콜은 SMB 프로토콜에 LanMana  gerNetBIOS 프로토콜까지 포함한다.

 

 (2) 삼바의 기능

   1) 리눅스파티션과 윈도우와의 공유

   2) 윈도우파티션과 리눅스와의 공유

   3) 리눅스의 프린터와 윈도우와의 공유

   4) 윈도우의 프린터와 리눅스와의 공유

 

2. 삼바(SAMBA)의 설치

(1) rpm패키지로 설치하기

   1) 설치의 확인

     rpm -qi samba  rpm -qa |grep samba 명령등을 내려 삼바가 설치되어 있는지 확인한다.

   

   2) 설치하기

    . 레드햇 리눅스계열을 설치했을 경우에는 CD-ROM드라이브를 마운트한다.

    . /RedHat/RPMS디렉토리로 이동한다.

    . rpm -Uvh samba* 명령을 내려 설치한다.

   

(2) 소스로 설치하기

   1) 만약 기존에 삼바가 설치되어 있으면 제거한다.

   2) cd /usr/local/src 명령을 내려 이동한다.

   3) 삼바의 공식사이트인 http://www.samba.org에서 소스파일(samba-2.2.5.tar.gz)를 다운받는다.

   4) tar zxvf samba-2.2.5.tar.gz 명령을 내린다.

   5) cd samba-2.2.5

   6) cd source

   7) ./configure --prefix=/usr/local/samba

   8) make

   9) make install

   10) 삼바소스디렉토리에서 examples라는 디렉토리의 smb.conf.default파일을
/usr/local/samba/      lib/smb.conf
파일로 복사한다.

     

   11) 삼바를 실행하는 스크립트파일을 /etc/rc.d/init.d/디렉토리에 복사한다. 삼바소스 디렉토리      에서packging/RedHat 디렉토리에 보면 삼바구동스크립트파일인 smb.init이 있는데 이 파일을 samba라는 이름으로 복사한다.
) cp smb.init /etc/rc.d/init.d/samba

       

   12) 데몬실행파일 PATH가 설정된 경로로 복사한다. 보통 소스로 설치하면 데몬을 실행하는 파일인 smbd nmbd/usr/local/samba/sbin디렉토리에 있다. 이 경로는 현재 PATH설정이 안 되어있 으므로 PATH를 설정하거나/usr/sbin디렉토리에 복사한다.
) cp smbd nmbd /usr/sbin

 

 

2. 삼바의 구성

(1) 데몬: 삼바를 이용하기 위해서는 두 개의 데몬이 필요하다.

   1) 기본데몬

    . smbd: SMB데몬 - 파일과 프린터공유, 사용자의 권한부여 및 확인

    . nmbd: 클라이언트를 위해 NetBIOS nameserver를 지원하고 browsing(서비스통지)한다.

   

   2) 스크립트 데몬파일: 실제 데몬을 구동하고 정지시키는 데몬으로 스타트시키면 smbd nmbd데몬을 띄운다.
 /etc/rc.d/init.d/smb start|stop

   

(2) 환경파일: smb.conf

   1) 개요: smb.conf는 삼바의 환경설정파일로 파일안의 구성은 win.ini와 비슷하다. 크게 Global Setting Share Denition으로 나누고 다시 각 내용은 [] 기호를 이용하여 섹션(Section)           으로 구분한다.

         

   2) 역할: 삼바를 제어하는 환경파일이다. 이 파일에서 외부와 공유시킬 시스템 자원과 어떠한
제약을 할 것인지를 지정한다.        

   3) 위치: 보통 /etc디렉토리에 위치하나 배포판에 따라 /etc/samba디렉토리에 위치할 수도 있다.

   4) 파일내부의 유형

    . #으로 시작하는 행: 이 행은 주석처리되어 무시된다.

    . ;으로 시작하는 행: 이 행들도 주석행으로 간주되어 무시된다. 보통 이런 행은 설정행이 작동하지 않도록 하는데 사용된다. ;대신에 #을 사용해도 된다.
이 두 가지 유형의 주석이 사용되는 이유는 #은 유닉스에서 사용하는 주석이고. ;는 윈도우에서 사용하는 주석이라 두 가지 모두 사용한다
.

    . [ ]을 사용하는 행: 섹션을 정의한다. 하나의 섹션이 정의된 후 그 다음 섹션이 정의될 때까지의 행들은 해당 섹션에 속한다.

    . name = value : 사용하는 옵션과 해당값을 설정하는 행들이다.

 

   5) 기본적인 섹션

    . [global]: 삼바서버의 전체적인 환경설정을 담당하는 섹션

    . [homes]: 사용자들이 자신의 홈디렉토리로 접근할 때의 권한을 설정하는 섹션

    . [printers]: 프린터관련 권한을 설정하는 섹션

 

(3) 실행파일

   1) smbclient

    . 설명: 유닉스를 위한 삼바클라이언트 명령어로 윈도우의 서버로 접근하기 위해 사용한다.

    . 사용법

       smbclient [option] [접속하고자할 호스트명]

    . option

       -L: 접속하고자할 호스트명을 입력한다.

       -U: 접속할 때의 사용자명을 입력한다.

    . 사용예

      a. [root@www root]# smbclient -L system2 -U administrator

          => system2라는 호스트에 administrator라는 권한으로 접속한다.

      b. [root@www root]# smbclient -L system2 -U administrator%1234

          => system2라는 호스트에 administrator라는 권한으로 접속하는데 패스워드는 1234이다.

      c. [root@www root]# smbcleint \\\\system2\work -U administrator

          => system2라는 호스트의 work라는 디렉토리를 administrator권한으로 접속한다.

   2) smbmount

    . 설명: 공유된 윈도우폴더를 유닉스에서 마운트할 때 사용하는 명령이다.

    . 사용법

       smbmount 윈도우호스트의_공유폴더 마운트할_디렉토리명 [-o option]

    . option: -o를 기본적으로 적어야 하고 key=value형태로 적는다.

       username: 사용자의 권한을 적는다.

    . 사용예

       [root@www root]# smbmount //system2/work /mnt/win -o username=administrator

        => system2 work라는 공유폴더를 /mnt/win이라는 디렉토리로 마운트한다. 권한자는

          administrator이다.

   3) smbprint: 삼바 호스트의 프린터에 프린트를 하기 위한 스크립트

   4) smbprint.sysv: smbprint와 같지만 System V계열 유닉스에서 사용한다.

   5) smbstatus: 현재의 삼바 연결상태를 보여준다.

   6) smbrun: 삼바 호스트의 응용프로그램 실행을 용이하게 하는 스크립트이다.

   7) testparm

    . 설명: 삼바의 환경설정파일인 smb.conf파일의 설정이 제대로 되었는지 확인하는 명령이다.

    . 사용법
testparm [
환경설정파일경로] [IP주소]

    . 사용예

      a. [root@www root]# testparm
=>
현재 서버에서 /etc/smb/smb.conf파일의 설정을 확인한다.

      b. [root@www root]# testparm /etc/smb/smb.conf 192.168.1.3
=> 192.168.1.3
 IP주소를 갖는 삼바서버의 /etc/smb/smb.conf파일의 설정을 확인한다.

 

   8) nmblookup

    . 설명: WINS 서버에 질의할 때 사용하는 명령이다.
DNS
서버에 질의하는 nslookup명령과 유사 하다.

    . 사용법

       nmblookup option

    . option

       -U: 서버이름을 지정한다. 보통 -R옵션과 같이 사용한다.

       -R: 조회할 이름을 지정한다.

    . 사용예

      a. [root@www root]# nmblookup samba.org -R 'IRIX#1B'
=> samba.org
라는 서버에 IRIX#1B라는 이름을 조회한다.

      b. [root@www root]# nmblookup \*
=>
브로드캐스트 영역에 있는 모든 이름을 조회한다.

 

3. smb.conf를 이용한 세팅

(1) 전체설정(Global Setting)

   1) workgroup = MYGROUP  => 윈도우의 작업그룹처럼 공유그룹을 지정한다.

   2) server string = Samba Server => 서버에 대한 설명을 부여해 준다.    

   3) hosts allow = 192.168.1. 192.168.2. 127.
=>
삼바 서버에 접속을 허용할 호스트를 지정하는데 사용한다. 접속할 허용할 호스트는 아이피 주소, 호스트명 등을 지정할 수 있으며, 네트워크/넷마스크형태로도 지정할 수 있다
.
또한 EXCEPT라는 키워드와 와일드문자도 사용가능하다. 참고로 삼바서버에 접속이 불가능하게 하려면 hosts deny로 설정한다
.
 
) 1. hosts allow = 192.168.1. 192.168.2. 127.
  => 192.168.1.0
네트워크대역에 속한 모든 호스트들과 로컬시스템(127.0.0.0네트워크
)
     
에서 접속이 가능하다.

           2. hosts allow = 192.168.1. EXCEPT 192.168.1.220 192.168.1.244
=> 192.168.1.220
 192.168.1.244를 제외한 192.168.1.0 네트워크에 속한 모든 호스트들이 삼바 서버에 접속할 수 있다
.

            3. hosts allow = 192.168.1.0/255.255.255.0
             => 192.168.1.0
네트워크에 속한 모든 호스트들의 접속을 허용한다.

             

            4. hosts allow = posein, xitem
              => posein
 xitem이라는 호스트들만 삼바 서버 접속을 허용한다.

             

   4) printcap name = /etc/printcap

     => 서버에 의해 사용되는 printcap name을 겹쳐쓰기 하고자 할 때 그 위치를 지정한다.

   

   5) load printers = yes

     => 삼바 서버의 printcap에 정의된 모든 프린터 목록이 자동적으로 로딩되게 할 것인가를 지정하는 옵션이다. 네트워크 프린터를 삼바서버에 연결하여 사용하고자 한다면 선택해야 한다.

     

   6) printing = lprng

     => 프린터 시스템 종류를 지정하는 옵션 비표준 프린터시스템이 아니면 지정할 필요가 없다.

   7) ;  guest account = pcguest

     => 공유 설정 섹션에서도 사용하는 항목으로, guest ok로 명시되어 있는 서비스에 접근할 유저를 지정해 준다. 보통 클라이언트가 손님서비스(guest service)에 접속할 수 있게 된다.
이러 한 유저는 /etc/passwd파일안에 존재하는 계정이어야 하며, 보통 "nobody"로 설정하면 된다
.

   8) log file = /var/log/samba/%m.log
     =>
삼바 서버에 접속하는 호스트의 접속 로그에 대한 기록을 저장하는 파일을 지정한다
.

   9) max log size = 0
     =>
로그 파일의 최대 크기를 KB단위로 제한두려고 할 때 사용되는 옵션이다. 제한을 두었을경

         
우에 이를 초과하게 되면 .old확장자를 가진 파일로 저장되고 새로운 파일이 생성된다.
         
현재처럼 0으로 설정하면 파일의 크기에 제한을 두지 않는다.

   10) security = user

      => 보안관련 옵션으로 클라이언트가 삼바서버에 접속할 때 인증 레벨을 부여하는 옵션이다.

        보안모드에는 4가지모드가 있는데 user, share, server, domain등이다. \

 

    * 4가지 모드
user:
삼바 서버에 접속하는 클라이언트는 먼저 반드시 윈도우 시작시 사용자명과 패스워드로 로그인을 한 후에 삼바 서버에 접속할 때 같은 사용자명으로 패스워드를 확인한 후에 접속이 이루어진다.

    share: 유효한 사용자명과 패스워드로 삼바 서버에 로그인을 하지 않아도 서버에 접속할 수있게 하므로, 삼바 서버 인증과정을 필요로 하지 않을 때 이 레벨로 지정하면 된다.
공유 디렉토리 접근에 제한을 두는 경우에 사용할 수 있다
.

    server: 윈도우 NT와 같은 다른 삼바 서버가 존재해야 하며, 다른 삼바 서버에 사용자명과
패스 워드를 전달하여 올바른지를 확인한다.

    domain: 윈도우 NT서버가 있어야 가능하며, 삼바서버가 사용자명과 패스워드를 윈도우NT
도메인 컨트롤러(Domain Controller)에 전달하여 유효한지 확인하는 방법이다.

   11) ;   password server = <NT-Server-Name>

    => 보안옵션에서 server domain값을 설정했을 경우에 윈도우 NT같은 다른 삼바서버의NetBIOS이름으로 지정한다.

   12) ;  password level = 8
       ;  username level = 8
    =>
패스워드 레벨과 유저 레벨을 설정하는 것으로 8개 문자를 사용하도록 설정한다
.

   13) encrypt passwords = yes
   =>
삼바서버에 클라이언트의 접속이 이뤄지는 과정에서 인증을 위하여 암호화 패스워드 옵션을

   
사용할 수 있다. 암호화된 패스워드를 지정하는 옵션이다.

   14) smb passwd file = /etc/samba/smbpasswd
    =>
이 옵션은 encrypt passwords항목과 같이 사용되는 것으로 암호화된 삼바 사용자의 아이디

   
와 패스워드가 기록되는 파일이다. 이 파일에 패스워드를 추가하는 명령은 smbpasswd이다.

   15) ;  unix password sync = Yes

       ;  passwd program = /usr/bin/passwd %u

       ;  passwd chat = *New*UNIX*password* %n\n *ReType*new*UNIX*password(생략)

    => 클라이언트 호스트에서 사용자의 패스워드를 변경할 수 있도록 해주는 옵션이다. , 이 경우에는 encrypt password, smb passwd file 두 옵션을 반드시 사용해야 한다.


   16) ;  username map = /etc/samba/smbusers

    => 리눅스 사용자 이름과 삼바 사용자 이름이 서로 다를 경우 서로 매핑시키기 위한 사용한다.

 

   17) ;   include = /etc/samba/smb.conf.%m

    => 접속하는 각 클라이언트마다 서로 다른 설정을 사용할 수 있게 해주는 것으로 %m은 접속하는 NetBIOS이름으로 대치된다.

   18) socket options = TCP_NODELAY SO_RCVBUF=8192 SO_SNDBUF=8192

    => 사용자의 로컬 네트워크상에서 삼바서버가 최적의 성능을 발휘할 수 있도록 튜닝할 때 사용
한다. IPTOS_LOWDELAY, IPTOS_THROUGHPUT 등의 옵션이 있다.

 

   19) ;   interfaces = 192.168.12.2/24 192.168.13.2/24

    => 삼바에 연결된 네트워크 인터페이스를 설정하는 것으로, 인터페이스는 IP/netmask조합으로 지정할 수 있다. , 위의 설명은 삼바서버가 서로 네트워크 인터페이스를 사용할 때 사용하는 것으로 192.168.12.2 호스트와 192.168.2.2 호스트에 연결을 허용한다는 의미이다.

   20) ;   remote browse sync = 192.168.3.25 192.168.5.255

       ;   remote announce = 192.168.1.255 192.168.2.44

   => 동기화시킬 원격 브라우저를 설정하는 부분이다.

   21) ;   local master = no

    => 이 옵션은 삼바가 nmbd에 의해 서브넷상에서 로컬마스터브라우저가 될 수 있도록 허용하는 것으로 no라고 설정하면 nmbd데몬은 서브넷상에서 로컬 마스터브라우저가 되지 않는다.

   22) ;   os level = 33

    => 삼바서버가 브라우저 선거에 있어서 자신을 알릴 수 있는 레벨을 설정하는 것으로 이 값에 의해서 nmbd데몬이 로컬브로드캐스트지역에서 WORKGROUP에 대해 로컬마스터 브라우저가 될 수 있는지 결정된다.
이 값을 0으로 설정하면, nmbd데몬은 윈도우 머신에 대해서 선거권을 상실하므로 로컬마스터 브라우저가 되질 못한다. (관련문서는
 BROWSING.txt)|

   23) ;   domain master = yes

    => 이 옵션은 삼바가 도메인 마스터 브라우저가 되도록 해준다. 이것은 삼바와 서브넷간의 브라우저 리스트를 모집할 수 있게 해준다. 만일 NT도메인 컨트롤러를 가지고 있다면 이 기능을 사용해서는 안된 다.

 

 

   24) ;   preferred master = yes|
    =>
삼바구동시 로컬 마스터 선거를 강요하여 선거에서 이길 수 있게 보다 많은 가능성을 부여해

   
주는 옵션이다. 이 옵션은 domain master = yes옵션과 같이 사용하여 nmbd데몬에 의해 도메인    
   
마스터가 될 수 있도록 해준다.

 

   25) ;   domain controller = <NT-Domain-Controller-SMBName>

    => 이 옵션은 호환성 문제로 현재는 사용하는 않는다.

 

   26) ;   domain logons = yes

    => 삼바서버가 WORKGROUP에 윈도우98 도메인 로그온 역할을 할 것인지의 여부를 지정한다.

   27) ;   logon script = %m.bat

       ;   logon script = %U.bat

    => 사용자가 성공적으로 로그인을 하였을 때 다운로드하여 작동할 수 있도록 배치파일(*.bat) 또는 NT명령파일(.cmd)을 지시해 주는 옵션이다. 배치파일은 마지막 줄에 cr/if가 들어있어야 하므로 도스편집기에서 만드는 것을 권장한다. 내용은 사용자가 임의대로 지정할 수 있다.

    ) 보통 모든 클라이언트 머신들이 서버와 똑같은 시간에 시간을 맞추도록 할 수 있다.
        NET TIME \\SERVER /SET /YES

 

   28) ;   logon path = \\%L\Profiles\%U

    => 윈도우98  NT에서 user.dat과 같은 로우밍 프로파일(roaming profile)을 어디에 지정할 것인가를 지정해 주는 옵션이다. %L은 서버의 NetBIOS이름으로 대치되고, %U는 사용자 이름으로 대치된다. 이 옵션을 사용할 때[Profile]공유 항목에서 주석을 풀어 주어야 한다.

 

   29) ;   wins support = yes

    => 삼바서버에서 nmbd데몬이 wins서버의 역할을 할 수 있는지 여부를 지정한다. 만일 이 옵션을 선택하기 위해서는 반드시 다중 서브넷 네트워크를 가지고 있어야 하며, wins서버로 될특정 nmbd데몬이 있어야 한다.

 

    (참고) WINS(Windows Internet Naming Service)

    마이크로소프트 윈도우NT 서버의 일부인 WINS는 각 구성 변경에 수반되는 사용자 또는 관리자가 없는 IP주소들과 컴퓨터 이름 및 위치들과의 결합을 관리한다. WINS는 컴퓨터 이름과 IP주소를 서로 매칭시켜 데이터를 테이블내에 자동으로 만드는데, 이 이름들은 다른 사람의 컴퓨터 이름과 중복되지 않도록 고유한 이름으로 견지한다. 컴퓨터가 다른 장소로 옮겨지면, IP주소의 서브넷 부분이 변경될 수 있다. WINS를 사용하면 새로운 서브넷 정보가 WINS테이블내에서 자동으로 갱신된다. WINS는 어떤 컴퓨터가 네트워크에 처음  정의될 때 IP주소를 협상하는 NT서버의 DHCP를 보충하여 완전하게 한다.
예를 들면 같은 네트워크에서 사람이 많아지면 네트워크에 부하도 많이 발생한다. 이 경우WINS를 사용하면 컴퓨터이름과 IP목록을 관리해주기 때문에 동보통신에 의한 부담을 줄일 수 있다.

 

   30) ;   wins server = w.x.y.z

    => wins 서버가 있을 경우 wins server IP값을 지정하는 옵션이다.

   31) ;   wins proxy = yes

    => nmbd에 의해서 wins기능을 갖추지 못한 호스트들을 대신하여 브로드캐스트 이름 질의를 대신 응답해 줄 수 있도록 지정해 주는 옵션이다. 이것을 사용하려면 네트워크상에 최소 하나 이상의 WINS서버가 있어야 한다.

   32) dns proxy = no
    => nmbd
데몬이 wins server역할을 하고, 등록되지 않는 NetBIOS이름을 찾아줄 때

    DNS server
를 사용하여 NetBIOS이름을 찾아줄 것인지의 여부를 지정하는 옵션이다.

   33) ;  case sensitive = no

    => 대소문자를 보존할 필요가 있을 때 설정하는 부분이다.

 

 

(2) 공유 정의(Share Definitions) : 삼바 서버에 접속할 수 있는 사용자의 홈디렉토리 설정   
1) [homes]

       comment = Home Directories

       browseable = no

       writable = yes

=> 사용자의 홈 디렉토리 서비스 사용을 위한 기본적인 설정을 해주는 항목이다. comment는 간단한 설명문, browseable은 공유이름을 브라우저에 표시할 수 있게 하는 기능, writable은 쓰기허용을 설정한다.

   2) [netlogon]

        comment = Network Logon Service

        path = /home/netlogon

        guest ok = yes

        writable = no

        share modes = no

=> 도메인 로그온을 사용하고자 할 때 사용한다. 일반적으로 사용하지 않는다.


   3) [Profiles]

       path = /home/profiles

       browseable = no

       guest ok = yes

    => 특정한 프로파일을 지정할 때 사용한다. 일반적으로 사용하지 않는다.

   4) [printers]

       comment = All Printers

       path = /var/spool/samba

       browseable = no

       guest ok = no

       writable = no

       printable = yes

    => 삼바프린터를 네트워크 공유프린터로 사용하고자 할 경우에 설정한다. BSD계열의 프린터 시스템을 사용하면 일일이 프린터를 정의하지 않아도 된다.

   5) [tmp]

       comment = Temporary file space

       path = /tmp

       read only = no

       public = yes

    => 여러 사람들이 파일을 공유할 목적으로 유효하게 사용할 수 있다. 현재 기본값인 /tmp는 임시작업공간 디렉토리이므로 /var/tmp처럼 다른 디렉토리를 만들어서 사용하도록 한다.

   6) [public]

       comment = Public Stuff

       path = /home/samba

       public = yes

       read only = yes

       write list = @staff

    => 공개적으로 접근이 가능한 디렉토리이지만 staff그룹에 있는 사용자들을 제외한 사용자들은 오직 읽기만 사용가능하다.

   7) 사용자정의 섹션

     ) [posein]

           comment = shared-files in posein directory         => 간단한 설명이다.

           path = /home/posein/pds          => 공유디렉토리의 경로를 지정한다.

           read only =no   => 공유디렉토리를 읽기만 가능하게 할지를 지정한다.

           writable = yes  => write ok = yes와 같은 옵션으로 쓰기가 가능하다.

           valid user = posein xitem prehee             =>서비스디렉토리에 사용가능한 사용자를 말하며, 만약 이 옵션이 생략되면 모든 사용자가 접근할 수 있다.

           public = no                 => guest ok와 같은 옵션으로 no로 설정하면 다른 사용자들은 이용할 수 없고 개인사용자만 사용할 수 있게 된다.

           browseable = no        => 이용 가능한 공유리스트를 보여줄 것인가를 지정하는 것으로 no로 지정하면 리스트를 보여주지 않는다.

           printable = no          => 서비스로 지정된 디렉토리에 스풀파일을 지정할 것인가를 지정                              는 것으로, 프린터 공유 디렉토리가 아니므로 대부분 no로 설정한다.

           create mask =0765        => create mode와 같은 옵션으로 파일을 생성할 때 사용되는 모드를 나타낸다.

        (참고) samba 에서 공유디렉토리를 지정할 때 사용하는 옵션 설명

              - read only : 공유 디렉토리를 읽기만 가능하게 할 것인지를 결정

              - writable, write ok : 공유 디렉토리를 쓰기 가능하게 할 것인지를 결정

              - valid users : 공유 디렉토리에 로그인할 수 있는 사용자를 결정

              - public, guest ok : 다른 사용자들이 이용하게 할 지를 결정

              - browseable : 공유 디렉토리의 리스트를 보여줄 지를 결정

              - printable : 공유 디렉토리에 스풀 파일을 지정할 것인지를 결정

              - path : 공유할 디렉토리의 절대경로를 지정

              - comment : 간단한 설명을 적음

              - create mask, create mode : 파일을 생성할 때의 모드를 결정. umask값형태로 지정.

              - write list : 쓰기가 가능한 특정 사용자를 지정


 

4. 삼바를 이용하여 공유하기

(1) 리눅스 드라이브를 윈도우와 공유하기

   1) /etc/samba/smb.conf파일의 편집

 #======================= Global Settings =====================================

    [global]

     workgroup = WORKGROUP   // 윈도우의 워크그룹과 동일한 그룹을 지정한다.

     server string = FILE SERVER  // 리눅스서버에 대한 설명을 적는다.

     hosts allow = 192.168.0. 203.247.51. 127.  // 접근허용할 네트워크 범위를 지정한다.

     security = share

 

 #============================ Share Definitions ==============================

    [public]

    comment = samba            // 간단한 설명

    path = /home/posein/samba   // 공유디렉토리 지정

    public = yes

    writable = yes

    printable = no

  (
참고) public으로 공유하고 writable = yes이면 공유한 해당디렉토리에 다른 사용자계층의 퍼미션

 
에서 쓰기권한을 부여해야 한다.

   2) 삼바 데몬을 다시 가동한다.

    /etc/rc.d/init.d/smb restart

   3) 삼바 서버에서 테스트하기

    [root@mybestone /root]# testparm

    Load smb config files from /etc/samba/smb.conf

    Processing section "[homes]"

    Processing section "[printers]"

    Processing section "[public]"

    Loaded services file OK.

    Press enter to see a dump of your service definitions

   4) /etc/hostname이라는 파일을 생성하여 윈도우에서 확인할 이름을 지정한다.

     ) linux

   5) 윈도우에서 확인하기

    네트워크환경 => linux 라는 컴퓨터이름이 생성된다. linux를 더블클릭해서 확인해보면 public 이라는 공유폴더가 보인다.

   6) smbstatus로 서버 체크하기

 [root@mybestone /etc]# smbstatus

 Samba version 2.2.0

 Service      uid      gid      pid     machine

 ----------------------------------------------

 public       nobody   nobody   14568   posein_note (192.168.0.3) Fri Jul 26 01:18:18 2002

 No locked files

 

(2) 리눅스 클라이언트에서 윈도우 공유 폴더 접근하기

   1) 윈도우 PC설정 (윈도우 2000기준)

    . 전체컴퓨터이름과 작업 그룹명 확인

       바탕화면의 [내컴퓨터]를 오른쪽버튼클릭한뒤 [네트워크식별]항목을 보고 확인한다.

        ) 전체컴퓨터이름: posein_note

            작업그룹: 신경회로망

    . 공유할 폴더선택 및 공유설정

    . 바탕화면의 [네트워크환경]의 등록정보에서 'Microsoft네트워크 파일 및 프린터공유'가 있는지 확인한다.

   2) 리눅스에서 윈도우 접근(1): smbclient명령의 이용

    . smbclient명령을 이용하여 정보확인하기

       [root@mybestone /etc]# smbclient -L posein_note -U administrator

       added interface ip=203.247.40.252 bcast=203.247.40.255 nmask=255.255.255.0

       added interface ip=192.168.1.1 bcast=192.168.1.255 nmask=255.255.255.0

       Got a positive name query response from 203.247.40.244 ( 203.247.40.244 )

       Password:

        => 패스워드를 입력하면 정보를 보여준다.

    . 접근하여 읽거나 쓰기작업하기

       [root@mybestone /etc]# smbclient \\\\posein_note\\work -U administrator

       added interface ip=203.247.40.252 bcast=203.247.40.255 nmask=255.255.255.0

       added interface ip=192.168.1.1 bcast=192.168.1.255 nmask=255.255.255.0

       Got a positive name query response from 203.247.40.244 ( 203.247.40.244 )

       Password:

       Domain=[신경회로망] OS=[Windows 5.0] Server=[Windows 2000 LAN Manager]

       smb: \>

        => 패스워드를 정확히 입력하면 위와 같이 프롬프트가 나타난다. ?를 입력하면 기본적으로

          사용할 수 있는 명령어의 리스트가 나타난다.

   3) 리눅스에서 윈도우접근(2): smbmount명령의 이용- mount명령어와 같은 형식으로 윈도우 공유

                               폴더와 그 폴더를 리눅스에서 마운트할 디렉토리가 필요하다.

    . 마운트한다.

    [root@mybestone /etc]# smbmount //posein_note/work /mnt/win -o username=administrator

    Password:

    => 패스워드를 입력하면 해당디렉토리로 마운트된다. /mnt/win이라는 디렉토리는 생성되어 있어야 하면 -o는 사용자이름을 지정하기 위한 옵션이다.

    . cd /mnt/win으로 이동하여 읽거나 쓰기를 한다.

 

5. 삼바 User레벨로 사용하기

(1) User레벨관련 주요 파일 분석

   1) /etc/samba/smb.conf

    . 설명: 삼바의 환경설정파일로 크게 두 개의 섹션으로 구분되어 있다.

    . Section

      a. [global] : 삼바와 관련된 전체적인 환경설정을 하는 부분으로

      b. [share definition] : 공유영역으로 여러 섹션이 존재한다. 주요 섹션은 다음과 같다.

        - [homes] : home디렉토리와 관련된 설정을 할 수 있다.

        - [printers] : 프린터와 관련된 설정을 할 수 있다.

        - [tmp] : /tmp 디렉토리와 관련된 설정을 할 수 있다.


   
. magic cookies: 삼바의 smb.conf에서도 아래의 magic cookies를 사용할 수 있다.

       %u : 현재 사용중인 유저

       %g : 현재 사용중인 유저 그룹

       %m : 클라이언트의 NetBIOS이름

       %v : 버전

       %h : 호스트이름

       %p : 서버의 홈디렉토리 경로

       %d : 서버의 프로세서ID

       %S : 현재 사용되는 서비스 이름

       %P : 현재 사용되는 서비스의 루트 디렉토리

       %U : 세션의 유저

       %G : 셔션의 유저그룹

       %H : %u로 주어진 유저의 홈디렉토리

       %L : 서버의 NetBIOS이름

       %M : 클라이언트 머신 이름

       %N : NIS 홈디렉토리 서버의 이름

       %I : 클라이언트 머신 IP

       %T : 날짜와 시간

   2) /etc/samba/smbusers : 삼바사용자들을 설정하는 파일이다.

   3) /usr/bin/smaadduser : 삼바사용자들의 패스워드를 등록하는 명령이다.

   4) /var/log/samba : 삼바의 로그가 기록되는 파일이다.

   5) /var/spool/samba : 프린터의 스풀디렉토리이다.

(2)  삼바서버의 인증레벨

   1) 설명: smb.conf파일의 [global]섹션의 security항목에서 설정한다.

   2) security 레벨의 설정

    . share : 인증과정을 거치지 않고 접근이 가능하다. smb.conf파일에서 hosts allow항목에 지정

               된 호스트라면 비밀번호없이 삼바를 사용할 수 있다.

    . user

      a. 설명: 사용자의 인증을 거치는 레벨이다.

      b. 윈도우 사용자의 접속법 : 삼바서버에 설정한 계정과 패스워드로 접속한다.

      c. smb.conf파일에서의 설정 
- [global]
섹션의 encrypt passwords = yes, smb passwd file = /etc/smbpasswd 설정을 추가로 해야 한다.

      d. 삼바의 사용계정: /etc/passwd에 있는 계정이어야 한다.

      e. 삼바사용자의 지정: smbpasswd 명령을 이용하여 사용자 계정과 비밀번호를 설정한다.

    . server

      a. 설명: 사용자 정보를 담고 있는 윈도우서버와 같이 다른 운영체제가 삼바서버에 대한 사용자와 패스워드를 인증을 전달하는 방법이다.

      b. smb.conf파일에서의 설정: password server 지시자에 인증할 서버의 IP를 적는다.

    . domain : 삼바서버가 윈도우 서버의 도메인 컨트롤러에 사용자명과 패스워드를 전달하여

                인증하는 방법으로 server레벨과 비슷하다.


 

(3) 삼바관련 명령어

   1) smbpasswd

    . 설명: 삼바서버의 사용자 계정을 만들거나 패스워드를 설정하는 명령이다.

    . 사용법

       smbpasswd option user_id

    . option

       -a : 사용자계정을 추가시에 사용한다.

       -x : 사용자계정을 삭제한다.

   2) smbadduser

    . 설명: 삼바서버의 사용자계정을 만드는 명령이다.

    . 사용법

       smbadduser 계정명:계정명

        => 콜론(:)을 기준으로 앞의 계정명은 Unix에서 사용할 이름이고 뒤의 계정명은 윈도우에서

          사용하는 계정명이다.

    . 사용예

       smbadduser posein:posein

        => posein이라는 사용자를 추가한다.

(4) user레벨로 삼바서버 사용하기

   1) 리눅스서버에서 설정하기

     . smb.conf파일 설정

        security = user                // user레벨로 변경

        encrypt passwords = yes       // 활성화시킨다.

        smb passwd file = /etc/samba/smbpasswd   // 활성화시킨다.

     . 사용자추가하기

   [root@www samba]# smbpasswd -a posein  
=> posein
이라는 계정을 추가하였다. 리눅스서버의 계정으로 한다.

 

      New SMB password:            // 패스워드를 설정한다. 반드시 리눅스서버의

                                      //패스워드와 같지 않아도 된다.

     Retype new SMB password:

     unable to open passdb database.

     Added user posein.

 

출처 : 정성재 강사님 (대전 국제IT교육센터)

크리에이티브 커먼즈 라이센스
Creative Commons License
2012/05/09 01:12 2012/05/09 01:12
받은 트랙백이 없고, 댓글이 없습니다.

댓글+트랙백 RSS :: http://blog.hwport.com/rss/response/658

댓글+트랙백 ATOM :: http://blog.hwport.com/atom/response/658


본 내용은 "http://studyfoss.egloos.com/5254916"에서 발췌한 것임을 밝힙니다.


----



glibc는 공개된 심볼(함수, 변수)들의 호환성을 보장하기 위해 각 심볼마다 버전을 부여한다.
간단한 hello world 프로그램을 gcc로 컴파일한 후에 다음과 같이 실행하면 이를 확인할 수 있다.

$ readelf -s a.out | head

Symbol table '.dynsym' contains 5 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 00000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     2: 00000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.0 (2)
     3: 00000000     0 FUNC    GLOBAL DEFAULT  UND puts@GLIBC_2.0 (2)
     4: 080484bc     4 OBJECT  GLOBAL DEFAULT   16 _IO_stdin_used
여기서 주의깊게 봐야할 부분은 3번 항목 puts@GLIBC_2.0 부분이다.
원래 소스 코드에서는 printf() 함수를 통해 "Hello world" 문자열을 출력하도록 했지만
컴파일러가 최적화 과정에서 좀 더 가벼운 puts() 함수로 대체하였기 때문에 그냥 printf와 동일하다고 봐도 될 것이다.
여기서 GLIBC_2.0 부분이 바로 심볼 버전에 해당하는 것이며,
puts() 함수는 2.0 버전 이후로 동작이 (좀 더 정확히는 ABI가) 변경되지 않았다는 것을 짐작할 수 있다.

이렇게 심볼마다 버전을 별도로 관리하게 되면 라이브러리가 수정된 이후에도
불필요하게 기존 프로그램을 재컴파일하지 않아도 되므로 기존 프로그램과의 호환성을 더 높일 수 있게된다.

간단한 예제를 하나 생각해 보자.
먼저 다음과 같은 함수를 제공하는 라이브러리를 하나 만든다.

dso-version.c:
int dso_2powerof(int order)
{
  return 1 << order;
}

버전을 지정하기 위해서는 map 파일이 필요하다.

dso-version.map:
LIBDSO_0.1 {
  global: dso_2powerof;
  local: *;
};

여기서는 버전 이름을 LIBDSO_0.1로 정했지만 이름 자체에 특별한 제약은 없다.
이제 이러한 버전이 적용된 공유 라이브러리를 만들려면 다음과 같이 빌드하면 된다.

$ gcc -shared -o libdso.so -fPIC -Wl,--version-script=dso-version.map dso-version.c

이제 이를 사용하는 프로그램을 하나 만들어보자.
사용하는 프로그램 입장에서는 버전에 대해서 고려할 필요가 없으니
다음과 같이 단순히 사용하면 된다. (귀찮으니 헤더 파일은 생략한다...)

dso-user.c:
#include <stdio.h>
#include <stdlib.h>

extern int dso_2powerof(int order);

int main(int argc, char *argv[])
{
  int n = 5, pow;

  if (argc > 1)
    n = strtol(argv[1], NULL, 10);
 
  pow = dso_2powerof(n);
  printf("2 to the power of %d is %d(%#010x)\n", n, pow, pow);
  return 0;
}

출력 결과는 다음과 같다.

$ gcc -o dso-v0.1 dso-user.c -L. -ldso -Wl,-rpath,.
$ ./dso-v0.1
2 to the power of 5 is 32(0x00000020)

dso-v0.1 파일에 기록된 버전 정보를 보려면 다음 명령을 이용하면 된다.

$ readelf -V dso-v0.1

Version symbols section '.gnu.version' contains 8 entries:
 Addr: 00000000080482ec  Offset: 0x0002ec  Link: 6 (.dynsym)
  000:   0 (*local*)       2 (LIBDSO_0.1)    0 (*local*)       0 (*local*)    
  004:   3 (GLIBC_2.0)     3 (GLIBC_2.0)     3 (GLIBC_2.0)     1 (*global*)   

Version needs section '.gnu.version_r' contains 2 entries:
 Addr: 0x00000000080482fc  Offset: 0x0002fc  Link: 7 (.dynstr)
  000000: Version: 1  File: libc.so.6  Cnt: 1
  0x0010:   Name: GLIBC_2.0  Flags: none  Version: 3
  0x0020: Version: 1  File: libdso.so  Cnt: 1
  0x0030:   Name: LIBDSO_0.1  Flags: none  Version: 2

버전 정보는 크게 두 가지로 나뉘는데
각 심볼에 따른 버전 정보는 .gnu.version 섹션에 저장되며
단순히 각 dynamic symbol에 해당하는 버전 정보를 정수값으로 저장한다.
위의 예제에서 총 8개의 심볼이 있는데 그 중 두 번째 항목이 LIBDSO_0.1 버전에 해당하는
정수값 2를 저장하고 있다. 이를 아래와 같이 심볼 테이블과 연관시켜보면 의미가 확실해 질 것이다.

$ readelf -s dso-v0.1 | head -12

Symbol table '.dynsym' contains 8 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 00000000     0 FUNC    GLOBAL DEFAULT  UND dso_2powerof@LIBDSO_0.1 (2)
     2: 00000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     3: 00000000     0 NOTYPE  WEAK   DEFAULT  UND _Jv_RegisterClasses
     4: 00000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.0 (3)
     5: 00000000     0 FUNC    GLOBAL DEFAULT  UND strtol@GLIBC_2.0 (3)
     6: 00000000     0 FUNC    GLOBAL DEFAULT  UND printf@GLIBC_2.0 (3)
     7: 080485ec     4 OBJECT  GLOBAL DEFAULT   16 _IO_stdin_used

이러한 심볼의 버전을 제공하는 라이브러리에 대한 정보는 .gnu.version_r 섹션에 저장되며
위의 예제에서는 GLIBC_2.0 버전을 제공하는 libc.so.6 (정수값 3)과
LIBDSO_0.1 버전을 제공하는 libdso.so (정수값 2) 의 두 라이브러리가 필요하다.

이렇게 한 동안 프로그램을 잘 사용하고 있었는데 어느날 버그를 발견했다.
dso_2powerof() 함수의 인자로 음수값이 들어가면 정의되지 않은(undefined) 동작이 발생할 수 있다.
gcc의 경우에는 rotate/shift right와 같이 동작하지만 이러한 특성에 의존하는 것은 올바르지 않다.

$ ./dso-v0.1 -5
2 to the power of -5 is 134217728(0x08000000)

그렇다면 라이브러리를 수정할 필요가 있다.
몇 가지 방법이 있을 수 있겠지만 일단 단순히 음수가 들어오는 경우에는 0을 반환하기로 하자.
이제 수정된 버전을 0.2라고 하면 libdso.so는 0.1과 0.2 버전을 모두 제공해야 하고
새로 링크되는 프로그램들은 기본적으로 0.2 버전을 사용하도록 알려줘야 한다.
이를 위해서는 다음과 같이 소스가 약간 지저분하게 변경되어야 한다.

dso-version.c:
int dso_2powerof_v2(int order)
{
  if (order < 0)
    return 0;
  return 1 << order;
}

extern int dso_2powerof_v1(int)
  __attribute__((alias("dso_2powerof_v2")));

asm (".symver dso_2powerof_v1,dso_2powerof@LIBDSO_0.1");
asm (".symver dso_2powerof_v2,dso_2powerof@@LIBDSO_0.2");

가장 중요한 부분은 맨 아래의 두 줄이다.
.symver라는 어셈블리 directive를 이용하여 버전을 지정할 수 있다.
자세히 보면 0.2 버전의 경우 @ 마크가 2개 붙어있는데 이는 0.2 버전이 기본적으로 사용된다는 것을 의미한다.

하지만 버전을 지정하려면 기존의 함수 이름을 변경해야 한다.
따라서 함수 이름 뒤에 (0.2 버전을 의미하는) _v2를 붙였다. (물론 아무 이름이나 선택해도 된다.)
0.2 버전의 경우에는 0.1 버전의 구현과 호환되기 때문에 예전 버전에서도 동일하게 동작하기 위해서
단순히 0.1 버전의 구현을 0.2 버전의 alias로 처리했다. (똑같은 함수를 두 번 구현할 필요가 없다!)

버전을 지정하는 map 파일도 마찬가지로 변경되어야 한다.

dso-version.map:
LIBDSO_0.1 {
  global: dso_2powerof;
  local: *;
};

LIBDSO_0.2 {
  global: dso_2powerof;
} LIBDSO_0.1;

LIBDSO_0.2 항목을 추가하여 dso_2powerof() 함수의 0.2 버전이 제공된다는 것을 지정한다.
LIBDSO_0.2는 LIBDSO_0.1의 다음 버전 임을 알리기 위해 마지막에 이전 버전을 적고
local 목록은 중복되지 않도록 LIBDSO_0.1 한 곳에만 적어준다.
(어차피 이들은 외부로 공개되지 않기 때문에 버전도 적용되지 않는다.)

이제 라이브러리와 프로그램을 다시 빌드하면 0.2 버전을 사용하도록 지정될 것이다.

$ gcc -shared -o libdso.so -fPIC -Wl,--version-script=dso-version.map dso-version.c
$ gcc -o dso-v0.2 dso-user.c -L. -ldso -Wl,-rpath,.
$ readelf -V dso-v0.2

Version symbols section '.gnu.version' contains 8 entries:
 Addr: 00000000080482ee  Offset: 0x0002ee  Link: 6 (.dynsym)
  000:   0 (*local*)       0 (*local*)       0 (*local*)       2 (GLIBC_2.0)  
  004:   3 (LIBDSO_0.2)    2 (GLIBC_2.0)     2 (GLIBC_2.0)     1 (*global*)   

Version needs section '.gnu.version_r' contains 2 entries:
 Addr: 0x0000000008048300  Offset: 0x000300  Link: 7 (.dynstr)
  000000: Version: 1  File: libdso.so  Cnt: 1
  0x0010:   Name: LIBDSO_0.2  Flags: none  Version: 3
  0x0020: Version: 1  File: libc.so.6  Cnt: 1
  0x0030:   Name: GLIBC_2.0  Flags: none  Version: 2

이제 프로그램을 음수 인자를 주어서 실행해보면 0이 반환됨을 알 수 있다.
0.2 버전을 이용하여 빌드한 새 프로그램은 물론이고
예전에 0.1 버전을 이용하여 빌드한 프로그램(dso-v0.1)도 역시 적용된다.

$ ./dso-v0.1 -5
2 to the power of -5 is 0(0000000000)

여기까지는 심볼 버전 관리의 장점이 크게 느껴지지 않는다.
하지만 함수의 기본형이 변경되는 (즉 더 이상 예전 버전과 호환되지 않는) 수정이 필요한 경우에는
라이브러리의 soname을 변경하여 전체 프로그램을 다시 빌드하지 않고도
해당 심볼의 여러 버전을 제공하여 호환성을 유지할 수 있다.

위의 예제에서 함수의 인자로 int형이 표현할 수 있는 비트 범위보다 큰 값이 들어오는 경우는 고려하지 않았다.
아마 이러한 동작도 undefined behavior일텐데 이에 대한 에러 처리가 필요하다.
0.2 버전에서 고려한 음수의 경우도 있고 해서 다음과 같이 구현을 수정하기로 하고 버전은 0.3으로 지정한다.

dso-version.c:
int dso_2powerof_v3(int order, int *result)
{
  if (order < 0 || order >= 8 * sizeof(int))
    return -1;
 
  if (result)
    *result = 1 << order;
 
  return 0;
}

int dso_2powerof_v2(int order)
{
  if (order < 0)
    return 0;
  return 1 << order;
}

extern int dso_2powerof_v1(int)
  __attribute__((alias("dso_2powerof_v2")));

asm (".symver dso_2powerof_v1,dso_2powerof@LIBDSO_0.1");
asm (".symver dso_2powerof_v2,dso_2powerof@LIBDSO_0.2");
asm (".symver dso_2powerof_v3,dso_2powerof@@LIBDSO_0.3");

map 파일은 0.2 버전의 경우와 동일하게 0.3 버전을 위한 항목을 만들면되므로 설명은 생략한다.
이제 프로그램도 변경된 API를 따라서 변경되어야 한다.

dso-user.c:
#include <stdio.h>
#include <stdlib.h>

extern int dso_2powerof(int order, int *result);

int main(int argc, char *argv[])
{
  int n = 5, pow;

  if (argc > 1)
    n = strtol(argv[1], NULL, 10);
 
  if (dso_2powerof(n, &pow) < 0)
    printf("dso_2powerof: invalid argument: %d\n", n);
  else
    printf("2 to the power of %d is %d(%#010x)\n", n, pow, pow);
 
  return 0;
}

이제 라이브러리와 프로그램을 다시 빌드하면 예전 버전과 최신 버전 모두 잘 동작한다.

$ gcc -shared -o libdso.so -fPIC -Wl,--version-script=dso-version.map dso-version.c
$ gcc -o dso-v0.3 dso-user.c -L. -ldso -Wl,-rpath,.
$ ./dso-v0.1 -5
2 to the power of -5 is 0(0000000000)
$ ./dso-v0.2 32
2 to the power of 32 is 1(0x00000001)
$ ./dso-v0.3 -5
dso_2powerof: invalid argument: -5
$ ./dso-v0.3 32
dso_2powerof: invalid argument: 32
$ ./dso-v0.3
2 to the power of 5 is 32(0x00000020)


=== 참고 문헌 ===
크리에이티브 커먼즈 라이센스
Creative Commons License
2012/04/18 16:14 2012/04/18 16:14
받은 트랙백이 없고, 댓글이 없습니다.

댓글+트랙백 RSS :: http://blog.hwport.com/rss/response/657

댓글+트랙백 ATOM :: http://blog.hwport.com/atom/response/657


  본 내용은 "http://korea.gnu.org/manual/release/ld/ld-sjp/" 에서 발췌한 것임을 밝힙니다.


-----

링커 스크립트(Linker Scripts)

ld 명령 언어는 문장(statement)들의 모임이다; 어떤 것들은 특별한 옵션을 설정하는 단순한 키워드들이고 어떤 것들은 입력 파일들이나 이름 출력 파일들을 선택하고 그룹핑하는 데 사용된다; 그리고 두 문장 타입들이 기초적이며 링크 프로세스에 널치 영향을 미친다.

ld 명령 언어의 가장 기초적인 명령은 SECTIONS 명령(see section 출력 섹션 지정(Specifying Output Sections))이다. 각 의미 있는 명령 스크립트는 SECTIONS 명령을 가져야 한다: 이것은 여러 등급의 자세한 내용들을 가진, 출력 파일의 레이아웃의 "그림(picture)"을 지정한다. 어떤 다른 명령도 모든 경우에 반드시 필요한 것은 아니다.

MEMORY 명령은 타겟 아키텍쳐에서 사용 가능한 메모리를 기술해서 SECTIONS 명령을 보완한다. 이 명령은 옵션이다; MEMORY 명령을 사용하지 않으면 ld는 모든 출력에 대해서 연속된 블럭으로 충분한 메모리가 사용가능할 것이라고 추정한다. See section 메모리 레이아웃(Memory Layout).

링커 스크립트에서 주석을 C에서처럼 넣을 수 있다; `/*'`*/' 안에 묶으면 된다. C에서 처럼 주석들은 문법적으로 공백과 동일하다.

Expressions

많은 유용한 명령들은 산술 표현식들을 포함한다. 명령 언어에 있는 표현식에 대한 문법은 다음과 같은 특성을 가지면서 C 표현식의 문법과 동일하다:

  • 모든 표현식은 정수로 평가되고 "long" 이나 "unsigned long" 타입이다.
  • 모든 상수들은 정수다.
  • C 산술 연산자들 모두가 제공된다.
  • 여려분은 전역 변수들을 참조하고, 정의하고, 생성할 수 있다.
  • 특수 목적의 내장 함수들을 호출할 수 있다.

정수(Integers)

8진수 정수는 `0' 뒤에 따라오고 0개 이상의 8진수 디지트 (`01234567')들로 이루어진다.

_as_octal = 0157255;

10진수 정수는 0이 아닌 디지트로 시작하고 0개 이상의 디지트들 (`0123456789')로 이루어진다.

_as_decimal = 57005;

16진수 정수는 `0x'`0X' 뒤에 하나 이상의 16진수 디지트들 `0123456789abcdefABCDEF'로 이루어진다.

_as_hex = 0xdead;

음의 정수를 쓰기 위해서 접두 연산자 `-' (see section 연산자(Operators)) 를 사용한다.

_as_neg = -57005;

이와 아울러 K 와 M가 상수를 각각 배씩 하는 데 사용될 수 있다. 예를 들어서 다음은 모두 동일한 양을 가리킨다:

        _fourk_1 = 4K;
        _fourk_2 = 4096;
        _fourk_3 = 0x1000;

심벌 이름(Symbol Names)

따옴표로 묶지 않으면 심벌 이름들은 문자, 밑줄, 또는 점으로 시작하고 임의의 문자들, 밑줄, 디지트, 점 그리고 하이픈을 담을 수 있다. 따옴표로 묶지 않은 심벌 이름들은 반드시 임의의 키워드와 충돌하면 안된다. 이상한 문자들을 담고 있거나 키워드와 동일한 이름을 담고 있는 심벌을, 심벌 이름을 겹따옴표로 싸서, 지정할 수 있다:

        "SECTION" = 9;
        "with a space" = "also with a space" + 10;

심벌들은 많은 비-알파벳 문자들을 담을 수 있기 때문에 심벌들을 공백들로 구분하는 것이 가장 안전하다. 예를 들어서 `A-B'는 하나의 심벌이다. 반면에 `A - B'는 빼기를 포함하고 있는 표현식이다.

위치 카운터(The Location Counter)

특수한 링커 변수 dot `.' 는 항상 현재 출력 위치 카운터를 담고 있다. .는 항상 출력 섹션에 있는 위치를 참조하기 때문에 이것은 항상SECTIONS 명령안에 있는 표현식에 나타나야 한다. . 심벌은 일반 심벌이 표현식에서 허용된 위치라면 어디든지 나타날 수 있지만, 이것의 할당(assignment)은 부작용을 가진다. 어떤 값을 . 심벌에 할당하는 것은 위치 카운터가 이동되도록 할 것이다. 이것은 출력 섹션에 구멍(hole)을 생성하는 데 사용될 수 있다. 위치 카운터는 뒤쪽으로 이동되어서는 안된다.

SECTIONS
{
  output :
  {
  file1(.text)
  . = . + 1000;
  file2(.text)
  . += 1000;
  file3(.text)
  } = 0x1234;
}

이전 예제에서 file1은 출력 섹션의 처음에 위치한다. 그 뒤에 1000바이트의 틈새가 있다. 그리고 file2 가 나타난다. 그리고 그 뒤에 또 1000바이트의 틈새가 있고 그 뒤에 file3가 로드된다. `= 0x1234'라고 표기하는 것은 어떤 데이터가 그 틈새에 기록되어야 할 것인가를 지정한다 (see section 옵션인 섹션 속성(Optional Section Attributes)).

@vfill

연산자(Operators)

링커는 다음과 같이 표준 바인딩과 우선순위 레벨들과 함께, 수식 연산자들의 표준 C 집합을 인식한다: { @obeylines@parskip=0pt@parindent=0pt @dag@quad Prefix operators. @ddag@quad See section 할당: 심벌 정의(Assignment: Defining Symbols). }

평가(Evaluation)

링커는 표현식들에 대해서 "게으른 평가(lazy evaluation)"를 사용한다; 이것은 절대적으로 필요할 때만 표현식을 계산한다. 링커는 임의의 링크를 하기 위해서 시작 주소의 값과 메모리 영역들의 길이를 필요로 한다; 이런 값들은 링커가 명령 파일을 읽을 때 가능한 한 빨리 계산된다. 그러나 다른 값들(예를 들어 심벌 값들)은 저장소 할당(stroage allocation)이 이루어지기 전까지는 알려지거나 않거나 필요하지 않다. 그런 값들은 나중에 평가된다. 다른 정보(출력 섹션들의 크기등과 같은)가 심벌 할당 표현식에서 사용될 수 있을 때에.

할당: 심벌 정의(Assignment: Defining Symbols)

여러분은 C 할당 연산자들 중 하나를 사용해서, 글로벌 심벌들을 생성할 수 있고 글로벌 심벌들에 값들(주소들)을 할당할 수 있다:

symbol = expression ;
symbol &= expression ;
symbol += expression ;
symbol -= expression ;
symbol *= expression ;
symbol /= expression ;

두 개의 것들이 ld 표현식들에서 다른 연산자들을 구분한다.

  • 할당은 표현식의 루트(root)에서만 사용될 수 있다; `a=b+3;' 는 허용되지만, `a+b=3;' 는 에러이다.
  • 여러분은 마지막 세미콜론 (";")을 할당 문장의 마지막에 놓아야 한다.

할당 문장들은 다음과 같이 보일 수 있다:

  • ld 스크립트에서 as commands in their own right in an ld script; or
  • as independent statements within a SECTIONS command; or
  • as part of the contents of a section definition in a SECTIONS command.

첫번째 두 경우들은 효력면에서 동일하다--둘다 절대 주소로 심벌을 정의한다. 두번째 경우는 주소가 특정한 섹션 (see section 출력 섹션 지정(Specifying Output Sections)) 에 상대적인 심벌을 정의한다.

링커 표현식이 평가되고 변수에 할당될 때 이것은 절대 또는 재배치 가능한 타입을 받는다. 절대 표현식 타입은 심벌이 출력 파일에 있게 될 값을 가지는 타입이다; 상대적 표현식 타입은 값이 섹션 베이스로부터의 고정된 오프셋으로 표현된다.

표현식의 타입은 스크립트 파일에서의 위치에 의해서 제어된다. 섹션 정의 안에서 할당된 심벌은 섹션의 베이스에 상대적으로 생성된다; 다른 장소에서 할당된 심벌은 절대적인 심벌로 생성된다. 섹션 정의안에서 생성된 심벌은 섹션의 베이스에 상대적이기 때문에 재배치 가능한 출력이 요구되면 이것은 재배치 가능하게 남을 것이다. 심벌은 절대 할당 함수 ABSOLUTE 를 사용하여 섹션 정의내에서 할당될 때에도, 절대 값으로 생성될 수 있다. 예를 들어서 주소가 .data이라는 이름의 출력 섹션의 마지막 바이트인 절대 심벌을 생성하기 위해서는:

SECTIONS{ ...
  .data : 
    {
      *(.data)
      _edata = ABSOLUTE(.) ;
    } 
... }

링커는 소스 표현식에 있는 모든 용어들이 알려질 때까지 할당의 평가를 꺼두려고(put off) 시도한다 (see section 평가(Evaluation)). 예를 들어서 섹션들의 크기는 할당 이후까지는 알려질 수 없다. 그래서 이런 것에 종속적인 할당은 할당 이후까지 수행되지 않는다. 위치 카운터dot, `.'에 종속적인 것과 같은 어떤 표현식들은 반드시 할당 도중에 평가되어야 한다. 표현식의 결과가 필요하다면 그러나 그 값이 사용 불가능이라면 에러가 발생한다. 예를 들어서 다음과 같은 스크립트는

SECTIONS { ...
  text 9+this_isnt_constant : 
    { ...
    }
... }

에러 메시지 "Non constant expression for initial address"를 발생시킨다.

어떤 경우 링커 스크립트가 심벌이 참조될 때만, 그리고 이것이 링크에 포함된 임의의 오브젝트에서도 정의되지 않았을 경우에만, 심벌을 정의하는 것이 바람직하다. 예를 들어서 전통적인 링커들은 심벌 `etext'를 정의한다. 그러나 ANSI C는 사용자가 `etext'를 에러 없이 함수로써 사용할 수 있기를 요구한다. PROVIDE 키워드는 `etext'과 같은 심벌을, 이것이 참조되지만 정의되지 않은 경우에만, 정의하는 데 사용될 수 있다. 문법은 PROVIDE(symbol = expression) 이다.

산술연산 함수(Arithmetic Functions)

명령 언어는 링크 스크립트 표현식들 안에서 사용되는 다수의 내장 함수들을 포함한다.

ABSOLUTE(exp)
이것은 표현식 exp의 절대값 (비-음수와는 반대로 재배치-불가)을 리턴한다. 이것은 심벌 값들이 일반적으로 섹션-상대적인 섹션 정의안에서 어떤 심벌에 절대값을 할당하는 데 주로 유용하다.
ADDR(section)
section이라는 이름의 절대 주소를 리턴한다. 여러분의 스크립트는 반드시 이전에 그 섹션의 위치를 정의해야 한다. 다음 예제에서symbol_1symbol_2는 동일한 값들로 할당된 것이다:
SECTIONS{ ...
  .output1 :
    { 
    start_of_output_1 = ABSOLUTE(.);
    ...
    }
  .output :
    {
    symbol_1 = ADDR(.output1);
    symbol_2 = start_of_output_1;
    }
... }
LOADADDR(section)
section라는 이름의 절대 로딩 주소(absolute load address)를 리턴한다. 이것은 일반적으로 ADDR와 동일하다. 그러나 이것은 AT라는 키워드가 섹션 정의에서 사용된다면 서로 다를 수 있다 (see section 옵션인 섹션 속성(Optional Section Attributes)).
ALIGN(exp)
다음 exp 바운더리에 정렬된 현재 위치 카운터 (.)의 결과를 리턴한다. exp는 반드시 그것의 값이 2의 몇제곱인 표현식이어야 한다. 이것은
(. + exp - 1) & ~(exp - 1)
과 동일하다. ALIGN은 위치 카운터의 값을 변경하지 않는다---이것은 단지 그것에 대해서 산술연산만을 수행한다. 예를 들어서 출력 .data섹션을 직전 섹션 뒤의 다음 0x2000 바이트 경계에 정렬하기 위해서, 그리고 그 섹션에 있는 어떤 변수를 입력 섹션들 뒤의 다음 0x8000경계에 정렬하기 위해서는:
SECTIONS{ ...
  .data ALIGN(0x2000): {
    *(.data)
    variable = ALIGN(0x8000);
  }
... }
예제의 ALIGN 첫번째 사용은 어떤 섹션의 위치를 지정한다. 왜냐면 이것은 섹션 정의의 옵션인 start 속성으로써 사용되었기 때문이다 (see section 옵션인 섹션 속성(Optional Section Attributes)). 두번째 사용은 단순하게 어떤 변수의 값을 정의한다. 내장 NEXTALIGN에 아주 밀접하게 연결되어 있다.
DEFINED(symbol)
링커의 글로벌 심벌 테이블안에 symbol이 있고 정의되어 있으면 1을 리턴한다. 그렇지 않으면 0을 리턴한다. 여러분은 이 함수를 사용해서 심벌들에 대한 디폴트 값들을 제공할 수 있다. 예를 들어서 다음 명령-파일 조각은 글로벌 심벌 begin.text 섹션에 있는 첫번째 위치에 설정하는 방법을 보여준다---그러나 begin이라고 불리는 어떤 심벌이 이미 존재한다면 그 값은 보존된다:
SECTIONS{ ...
  .text : {
    begin = DEFINED(begin) ? begin : . ;
    ...
  }
... }
NEXT(exp)
exp의 배수인 할당되지 않은 주소를 리턴한다. 이 함수는 ALIGN(exp)에 밀접하게 연결되어 있다; 여러분이 비연속(discontinuous) 메모리를 출력 파일을 위해 정의하기 위해서 MEMORY 명령을 사용하지 않는다면 두 함수들은 서로 동일하다.
SIZEOF(section)
section라는 이름을 가진 섹션의 크기를, 그 섹션이 할당되었다면, 바이트 단위로 리턴한다. 다음 예제에서 symbol_1symbol_2는 동일한 값들로 할당된다:
SECTIONS{ ...
  .output {
    .start = . ;
    ...
    .end = . ;
    }
  symbol_1 = .end - .start ;
  symbol_2 = SIZEOF(.output);
... }
SIZEOF_HEADERS
sizeof_headers
출력 파일의 헤더들의 크기를 바이트 단위로 리턴한다. 여러분은, 페이징을 쉽게 하기로 선택했다면, 이런 숫자를 첫번째 섹션의 시작 주소로써 사용할 수 있다.
MAX(exp1, exp2)
exp1exp2의 최대값을 리턴한다.
MIN(exp1, exp2)
exp1exp2의 최소값을 리턴한다.

세미콜론(Semicolons)

세미콜론 (";")은 다음과 같은 장소에서 필요하다. 모든 다른 장소에서 그들은 심미적 이유(aesthetic reasons)로 나타날 수 있지만 그렇지 않다면 무시된다.

Assignment
세미콜론은 할당 표현식 뒤에 반드시 나타나야 한다. See section 할당: 심벌 정의(Assignment: Defining Symbols)
PHDRS
세미콜론은 PHDRS 문장의 마지막에 나타나야 한다. See section ELF 프로그램 헤더(ELF Program Headers)

메모리 레이아웃(Memory Layout)

링커의 디폴트 설정은 모든 사용 가능한 메모리의 할당을 허용한다. 여러분은 이 설정을 MEMORY 명령을 사용해서 오버라이드할 수 있다.MEMORY 명령은 타겟내의 메모리 블럭들의 크기와 위치를 기술한다. 이것을 주의깊게 사용함으로써 여러분은 어떤 메모리 영역들이 링커에 의해서 사용될 수 있는지 그리고 어떤 메모리 영역들을 반드시 피해야 하는지 기술할 수 있다. 링커는 사용가능한 영역들에 맞추기 위해서 섹션들을 서로 섞지 않지만 요구된 섹션들을 정확한 영역들로 이동시키기는 한다. 그리고 그 영역들이 가득 차게 되면 에러들을 발생한다.

명령 파일은 많아야 한 번 MEMORY 명령을 사용할 수 있다; 그러나 여러분은 원하는 만큼 그 안에 있는 메모리 블럭들을 정의할 수 있다. 문법은 다음과 같다:

MEMORY 
  {
    name (attr) : ORIGIN = origin, LENGTH = len
    ...
  }
name
이것은 링커에 의해서 영역에 대한 참조를 하기 위해서 내부적으로 사용되는 이름이다. 임의의 심벌 이름도 사용될 수 있다. 영역 이름들은 분리된 이름 공간에서 저장되고 심벌들, 파일 이름들 또는 섹션 이름들과 충돌하지 않을 것이다. 서로 다른 이름들을 사용해서 여러 영역들을 지정하자.
(attr)
이것은 특정한 메모리를 링커 스크립트에 없는 섹션을 놓기 위해서 사용할 것인가 말것인가를 지정하는, 옵션인 속성들의 리스트이다. 유효한 속성 리스트는 섹션 속성들과 일치하는 "ALIRWX" 문자들로 구성되어야 한다. 속성 리스트를 생략하면 여러분은 이것을 둘러싸는 괄호들도 생략할 수 있다. 현재 지원되는 속성들은 다음과 같다:
`Letter'
Section Attribute
`R'
읽기-전용 섹션
`W'
읽기/쓰기 섹션
`X'
실행 코드를 담고 있는 섹션
`A'
할당된 섹션
`I'
초기화된 섹션
`L'
I와 동일.
`!'
다음에 오는 속성 의미의 반대.
origin
이것은 물리적 메모리에 있는 영역의 시작 주소이다. 이것은 메모리 할당이 이루어지기 전에 상수로 반드시 평가되어야 하는 표현식이다. 키워드 ORIGINorgo로 약식 표현될 수 있다 (그러나 예를 들어 `ORG'은 아니다).
len
이것은 해당 영역(표현식)의 바이트 단위 크기이다. 키워드 LENGTHlenl로 약식 표현될 수 있다.

예를 들어서 메모리가 할당---하나는 0부터 시작해서 256 KB를, 다른 것은 0x40000000 부터 시작해서 4 MB를--을 위해서 사용 가능한 두 영역들을 사용한다고 지정하려고 한다고 가정하자. rom 메모리 영역은 읽기-전용이거나 코드를 담고 있는 명시적인 메모리 레지스터 없이 모든 섹션들을 획득할 것이지만 ram 메모리 영역은 그 섹션들을 획득할 것이다.

MEMORY 
  {
  rom (rx)  : ORIGIN = 0, LENGTH = 256K
  ram (!rx) : org = 0x40000000, l = 4M
  }

일단 mem라는 이름의 메모리 영역을 정의했다면, SECTIONS 명령에서 `>mem'로 끝나는 명령을 사용함으로써 특정한 출력 섹션을 다른 곳으로 보낼수 있다 (see section 옵션인 섹션 속성(Optional Section Attributes)). 어떤 영역에 보내져서 결합된 출력 섹션들이 그 영역에 대해서 너무 크다면 링커는 에러 메시지를 출력할 것이다.

출력 섹션 지정(Specifying Output Sections)

SECTIONS 명령은 어디에 입력 섹션들이 출력 섹션들로 정확하게 놓일 것인가와 출력 파일에서의 그들의 순서, 그리고 그들이 할당된 출력 섹션들이 무엇인가를 제어한다.

여러분은 많아야 한번 SECTIONS 명령을 스크립트 파일에서 사용할 수 있다. 그러나 그 안에 여러분이 원하는 만큼의 문장들을 넣을 수 있다.SECTIONS 명령 안에 있는 문장들은 다음과 같은 것들 중의 하나가 될 수 있다:

  • 엔트리 포인트를 정의;
  • 심벌에 값을 할당;
  • 이름이 있는 출력 섹션의 위치, 그리고 어떤 입력 섹션들이 그 안으로 갈 것인가를 기술.

첫번째 두 작업들---엔트리 포인트를 정의하고 심벌들을 정의하는 것---을 SECTIONS 명령 바깥에서 사용할 수 있다: see section 엔트리 포인트(The Entry Point) 그리고 section 할당: 심벌 정의(Assignment: Defining Symbols). 그들은 스크립트를 읽기 쉽도록 하는 편의를 위해서 여기에 허용되었다. 그래서 그 심벌들과 엔트리 포인트는 여러분의 출력-파일 레이아웃의 의미 있는 위치들에서 정의될 수 있다.

SECTIONS 명령을 사용하지 않는다면 링커는 각 입력 섹션을 동일한 이름의 출력 섹션에, 그 섹션들이 입력 파일들에서 처음으로 나타난 순서대로 놓는다. 모든 입력 섹션들이 첫번째 파일에 다 있으면, 예를 들어서 출력 파일의 섹션들의 순서는 첫번째 입력 파이르이 순서와 일치할 것이다.

섹션 정의(Section Definitions)

SECTIONS 명령에서 가장 자주 사용되는 문장은 section definition이다. 이것은 출력 섹션의 속성들을 지정한다: 이것의 위치, 할당, 내용, 채우기 패턴, 그리고 타겟 메모리 영역. 이런 스펙의 대부분은 옵션이다; 어떤 섹션 정의 가장 간단한 형태는 다음과 같다

SECTIONS { ...
  secname : {
    contents
  }
... }

secname은 출력 섹션의 이름이고 contents는 거기에 들어갈 것이 무엇인가에 대한 스펙이다---예를 들어서 입력 파일들의 리스트나 입력 파일들의 섹션들 (see section 섹션 놓기(Section Placement)). secname 주변에는 공백 문자가 있어야 한다. 그렇게 섹션 이름이 구분된다. 다른 공백문자는 옵션이다. 그러나 콜론 `:'과 중괄호 `{}'가 필요하다.

secname는 반드시 여러분의 출력 포멧의 제약 조건(constraints)과 일치해야 한다. a.out와 같이 제한된 개수의 섹션들만을 지원하는 포멧에서 그 이름은 반드시 그 포멧에 의해서 지원되는 이름들(예를 들어서 a.out.text, .data, 또는 .bss만을 허용한다) 중의 하나이어야 한다. 출력 포멧이 임의 개수의 섹션들을 지원하지만 임의 개수만 허용하고 이름들은 그렇지 않다면(Oasys와 같은 경우), 이름은 반드시 따옴표로 묶인 숫자 문자열(quoted numeric string)으로 제공되어야 한다. 섹션 이름은 임의의 문자열로 이루어질 수 있지만 표준 ld 심벌 이름 문법에 맞지 않는 이름은 반드시 따옴표로 묶여야 한다. See section 심벌 이름(Symbol Names).

특수한 secname`/DISCARD/'는 입력 섹션들을 버리는 데 사용될 수 있다. `/DISCARD/'라는 이름의 출력 섹션에 할당된 임의의 섹션들은 마지막 링크 출력에 포함되지 않는다.

링커는 내용을 가지지 않는 출력 섹션들을 생성하지 않는다. 이것은 존재하거나 존재하지 않을 수 있는 입력 섹션들을 참조할 때 편의를 위한 것이다. 예를 들어서,

.foo { *(.foo) }

이것은 적어도 한 개의 입력 파일에 `.foo' 섹션이 존재한다면 출력 파일에 `.foo' 섹션을 생성할 뿐이다.

섹션 놓기(Section Placement)

섹션 정의에서 특정 입력 파일들을 넣어서, 특정 입력-파일 섹션들을 넣어서, 또는 이 두 가지를 합해서, 출력 섹션 섹션의 내용물을 지정할 수 있다. 여러분은 또한 이 섹션에 임의 데이터를 놓을 수 있고 섹션 시작에 상대적인 심벌들을 정의할 수 있다.

섹션 정의의 contents은 다음과 같은 종류의 문장들 중 임의의 것을 포함할 수 있다. 공백 문자로 다른 것들과 구분된, 단일 섹션 정의에 이들 중 원하는 만큼 포함할 수 있다.

filename
단순하게 현재 출력 섹션에 놓일 수 있도록 특정한 입력 파일의 이름을 지정할 수 있다; @이 파일에서 emph{모든} 섹션들은 현재 섹션 정의에 놓일 것이다. 그 파일 이름이 명시적인 섹션 이름 리스트와 함께 이미 다른 섹션 정의에서 언급되었다면 아직 할당되지 않은 그런 섹션들이 사용된다. 특정한 파일들 리스트를 이름으로 지정하려면:
.data : { afile.o bfile.o cfile.o }
이 예제는 다수의 문장들이 섹션 정의 내용물안에 포함될 수 있다는 것도 예시한다. 왜냐면 각 파일 이름이 분리된 문장이기 때문이다.
filename( section )
filename( section , section, ... )
filename( section section ... )
현재 출력 섹션에 삽입하기 위해서 입력 파일들로부터 하나 이상의 섹션들의 이름을 지정할 수 잇다. 입력-파일 섹션들 리스트를 괄호 안에 지정하고자 한다면 그 섹션 이름들을 공백 문자로 구분하자.
* (section)
* (section, section, ...)
* (section section ...)
링크 제어 스크립트안에서 명시적으로 입력 파일들의 이름을 지정하는 대신에 ld 명령 라인으로부터 모든 파일들을 참조할 수 있다:`*'를 괄호로 묶인 입력-파일 섹션 리스트 앞에 특정한 파일 이름 대신 사용한다. 이미 이름으로 명시적으로 포함된 어떤 파일들을 가지고 있다면 `*'은 모든 남은 파일들을 참조한다---출력 파일에 놓일 위치가 아직 정의되지 않은 것들. 예를 들어서 Oasys 파일로부터 1에서 4까지 섹션들을 a.out 파일의 .text 섹션에 복사하려면 그리고 1314 섹션들을 .data 섹션에 복사하려면:
SECTIONS {
  .text :{
    *("1" "2" "3" "4")
  }
  
  .data :{
    *("13" "14")
  }
}
`[ section ... ]'는 모든 할당되지 않은 입력 파일들로부터 이름이 있는 섹션들을 지정하기 위한 다른 대안으로써 종종 받아들여진다. 어떤 다른 운영 체제(VMS)는 파일 이름들에 각괄호들을 허용하기 때문에 그 표기법은 더이상 지원되지 않는다.
filename( COMMON )
*( COMMON )
이것은 출력 파일의 어디에다 초기화되지 않은 데이터를 이 표기법으로 놓을 것인가를 지정한다. *(COMMON) 홀로 모든 입력 파일들(할당되지 않는 한)로부터 초기화되지 않은 데이터를 참조한다; filename(COMMON)는 특정한 파일로부터 초기화되지 않은 데이터를 참조한다. 둘 다 어디에 입력-파일 섹션들을 놓을 것인가를 지정하는 일반적인 메카니즘들의 특별한 경우들이다: ld는 초기화되지 않은 데이터를 입력 파일의 포멧에 상관없이 COMMON 이라는 이름의 입력-파일 섹션안에 있는 것처럼 이것을 참조할 수 있도록 허용한다.

특정 파일이나 섹션 이름을 사용할 수 있는 임의의 위치에 또한 와일드카드 패턴을 사용할 수 있다. 링커는 유닉스 쉘이 그러한 것처럼 와일드카드들을 처리한다. `*' 문자는 임의 개수의 문자들에 대응한다. `?' 문자는 단일 문자에 대응한다. `[chars]' chars 중 임의의 문자 단일 인스턴스와 대응할 것이다; `-' 문자는 문자들의 범위를 지정하는 데 사용될 수 있다. `[a-z]'가 임의의 소문자와 대응하는 것처럼 말이다. `\' 문자는 다음 문자를 인용(quote)하는 데 사용될 수 있다.

파일 이름이 와일드카드와 대조될 때 와일드카드 문자들은 `/' 문자와 비교되지 않을 것이다(유닉스에서 디렉터리 이름들을 구분하는 데 사용되는 문자). 단일 `*' 문자로 이루어진 패턴은 예외이다; 이것은 항상 임의의 파일 이름과 일치된다. 섹션 이름에서 와일드카드 문자들은 `/'문자와 비교될 것이다.

와일드카드들은 명령행에서 명시적으로 지정된, 파일들만 찾는다. 링커는 와일드카드들을 확장해서 디렉터리들을 검색하지 않는다. 그러나 링커 스크립트 안에서 단순한 파일 이름을 지정한다면---와일드카드를 전혀 가지지 않는 이름--- 그리고 그 파일 이름이 또한 명령행에서 지정되지 않았다면 링커는 그 파일이 명령행에 나타난 것처럼 그 파일을 열려고 시도할 것이다.

다음 예제에서 명령 스크립트는 출력 파일을 세개의 연속된 섹션들, .text, .data, 그리고 .bss안으로 정렬한다. 이들 각각에 대한 입력으로 모든 입력 파일들 중에서 대응된 이름을 가진 것을 취해서.

SECTIONS { 
  .text : { *(.text) }
  .data : { *(.data) } 
  .bss :  { *(.bss)  *(COMMON) } 
} 

다음 예제는 all.o 파일로부터 섹션들 모두를 읽어서 그들을 0x10000 위치에서 시작하는 출력 섹션 outputa의 시작에 놓는다. foo.o 파일로부터 온 .input1 섹션 모두는 동일한 출력 섹션에서 즉각 뒤따라 온다. foo.o.input2 섹션 모두는 출력 섹션 outputb으로 들어가고 바로 뒤에 foo1.o.input1 섹션이 뒤따른다. 임의 파일에서 온 남은 .input1.input2 섹션들은 출력 섹션 outputc에 기록된다.

SECTIONS {
  outputa 0x10000 :
    {
    all.o
    foo.o (.input1)
    }
  outputb :
    {
    foo.o (.input2)
    foo1.o (.input1)
    }
  outputc :
    {
    *(.input1)
    *(.input2)
    }
}

이 예제는 와일드카드 패턴들이 파일들을 나누는 데 어떻게 사용되는가를 보여준다. 모든 .text 섹션들은 .text에 놓이며 모든 .bss 섹션들은.bss에 놓인다. 대문자로 시작하는 모든 파일들에 대해서 .data 섹션은 .DATA에 놓인다; 모든 다른 파일들에 대해서 .data 섹션은 .data에 놓인다.

SECTIONS {
  .text : { *(.text) }
  .DATA : { [A-Z]*(.data) }
  .data : { *(.data) }
  .bss : { *(.bss) }
}

섹션 데이터 표현식(Section Data Expressions)

데이터는 입력 파일들에서 가져 와서 여러분의 출력 파일에 앞에서 말한(foregoing) 문장들이 정렬한다. 데이터를 직접 링크 명령 스크립트로부터 출력 섹션에 놓는 것이 가능하다. 이런 추가의 문장들 대부분이 표현식들을 포함한다 (see section Expressions). 비록 이런 문장들이 보여주기 편하게 분리되어 나타나 있지만 그런 분리는 SECTIONS 명령의 섹션 정의안에서 전혀 불필요하다; 여러분은 우리가 방금 기술한 문장들 중에서 어떤 것도 자유로이 서로 섞을 수 있다.

CREATE_OBJECT_SYMBOLS
이것은 현재 섹션에 각 입력 파일에 대한 심벌을 생성하고 그 입력 파일로부터 작성된 데이터의 첫번째 바이트의 주소로 설정한다. 예를 들어서 a.out 파일들의 경우 각 입력 파일에 대한 심벌을 가지는 것이 편하다. 여러분은 이것을, 출력 섹션 .text를 다음과 같이 정의함으로써, 성취할 수 있다:
SECTIONS {
  .text 0x2020 :
     {
    CREATE_OBJECT_SYMBOLS
    *(.text)
    _etext = ALIGN(0x2000);
    }
  ...
}
sample.ld가 이 스크립트를 포함한 파일이라면, a.o, b.o, c.o, 그리고 d.o는 다음과 같은 내용을 가진 네 개의 입력 파일들이라면---
/* a.c */

afunction() { }
int adata=1;
int abss;
`ld -M -T sample.ld a.o b.o c.o d.o' 는 다음과 같이 오브젝트 파일 이름들과 일치하는 심벌들을 가지는 맵 파일을 생성할 것이다:
00000000 A __DYNAMIC
00004020 B _abss
00004000 D _adata
00002020 T _afunction
00004024 B _bbss
00004008 D _bdata
00002038 T _bfunction
00004028 B _cbss
00004010 D _cdata
00002050 T _cfunction
0000402c B _dbss
00004018 D _ddata
00002068 T _dfunction
00004020 D _edata
00004030 B _end
00004000 T _etext
00002020 t a.o
00002038 t b.o
00002050 t c.o
00002068 t d.o
symbol = expression ;
symbol f= expression ;
symbol은 임의의 심벌 이름이다 (see section 심벌 이름(Symbol Names)). "f="는 산술 및 할당을 합성한 &= += -= *= /= 연산자들 중의 임의의 것을 가리킨다. 특정 섹션 정의 안에서 심벌에 값을 할당할 때 그 값은 섹션의 시작에 상대적이다 (see section 할당: 심벌 정의(Assignment: Defining Symbols)). 다음과 같이 작성하였다면
SECTIONS {
  abs = 14 ;
  ...
  .data : { ... rel = 14 ; ... }
  abs2 = 14 + ADDR(.data);
  ...
}
absrel는 동일한 값을 가지지 않는다; relabs2와 동일한 값을 가진다.
BYTE(expression)
SHORT(expression)
LONG(expression)
QUAD(expression)
SQUAD(expression)
섹션 정의에 이런 네 문장들 중 하나를 포함함으로써 명시적으로 그 섹션의 현재 주소에 1, 2, 4, 8 unsigned 또는 8 signed 바이트들을 놓을 수 있다. 64 비트 호스트나 타겟을 사용한다면 QUADSQUAD는 동일하다. 호스트와 타겟이 둘 다 32비트이라면 QUAD는 unsigned 32 비트 값을 사용하고 SQUAD 기호는 그 값을 확장한다. 둘 다 그 값ㅇㄹ 작성할 때 정확한 endianness를 사용할 것이다. 멀티-바이트 값들은 바이트 순서가 출력 파일 포멧에 적절하다면 무엇이든 그 바이트 순서로 표현된다 (see section BFD).
FILL(expression)
현재 섹션에 대해서 "채우기 패턴(fill pattern)"을 지정한다. 그 섹션안에서 그렇지 않으면 지정되지 않았을 메모리 영역들(예를 들어서 위치 카운터 `.'에 새로운 값을 할당함으로써 건너 뛴 영역들)은 expression 매개변수로부터 온 두 LSB(least significant bytes)들로 채워진다. FILL 문장은 섹션 정의에 있는 위치 뒤에 메모리 위치들을 커버한다; 한 개 이상의 FILL 문장을 포함함으로써 출력 섹션의 서로 다른 부분들을 패턴들로 채울 수 있다.

옵션인 섹션 속성(Optional Section Attributes)

다음은 모든 옵션인 부분들을 포함한, 섹션 정의의 완전한 문법이다:

SECTIONS {
...
secname start BLOCK(align) (NOLOAD) : AT ( ldadr )
  { contents } >region :phdr =fill
...
}

secnamecontents이 필요하다. contents에 대해서는 See section 섹션 정의(Section Definitions), 그리고 section 섹션 놓기(Section Placement). 남은 요소들---start, BLOCK(align), (NOLOAD), AT ( ldadr ), >region, :phdr, 그리고 =fill---은 모두 옵션이다.

start
start 더하기 섹션 이름을 지정함으로써 출력 섹션이 지정된 주소로 로딩되도록 강제할 수 있다. start는 임의의 표현식으로 표현될 수 있다. 다음 예제는 output 섹션을 0x40000000에 생성한다:
SECTIONS {
  ...
  output 0x40000000: {
    ...
    }
  ...
}
BLOCK(align)
위치 카운터 .를 섹션 시작점보다 앞서게 하여 그 섹션이 지정된 정렬에서 시작하도록 하는 BLOCK() 스펙을 포함할 수 있다. align는 표현힉이다.
(NOLOAD)
`(NOLOAD)' 지시어는 실시간에 어떤 섹션이 로드되지 않도록 마킹한다. 링커는 그 섹션을 정상적으로 처리할 것이지만 그것을 마킹해서 프로그램 로더가 그것을 메모리로 로드하지 않도록 한다. 예를 들어서 아래의 스크립트 샘플에서 ROM 섹션은 메모리 위치 `0'에 위치하고 프로그램이 실행할 때는 로드되지 않아야 한다. ROM 섹션의 내용물들은 링커 출력 파일에서 일반적인 경우와 같이 나타날 것이다.
SECTIONS {
  ROM  0  (NOLOAD)  : { ... }
  ...
}
AT ( ldadr )
AT 키워드 뒤에 따라 오는 표현식 ldadr는 그 섹션의 로드 주소를 지정한다. 디폴트(AT 키워드를 사용하지 않는다면)는 위치 재지정 주소와 동일하다. 이 기능은 ROM 이미지를 빌드하기 쉽도록 고안된 것이다. 예를 들어서 이 SECTIONS 정의는 두가지 출력 섹션들을 생성한다: 하나는 `.text'라고 불리는 것이며 이것은 0x1000에서 시작한다. 그리고 다른 하나는 `.mdata'라고 불리는 것이며 이것의 위치 재지정 주소가 0x2000일지라도 `.text' 섹션의 끝에 로드된다. 심벌 _data은 code{0x2000} 값으로 정의된다:
SECTIONS
  {
  .text 0x1000 : { *(.text) _etext = . ; }
  .mdata 0x2000 : 
    AT ( ADDR(.text) + SIZEOF ( .text ) )
    { _data = . ; *(.data); _edata = . ;  }
  .bss 0x3000 :
    { _bstart = . ;  *(.bss) *(COMMON) ; _bend = . ;}
}
이런 식으로 생성된 ROM과 함께 사용하기 위해서, 초기화 데이터를 ROM 이미지로부터 이것의 실시간 주소로 복사하기 위해서, 실-시간 초기화 코드(C 프로그램들의 경우 보통 crt0)는 다음과 같이 어떤 것을 포함해야 한다:
char *src = _etext;
char *dst = _data;

/* ROM has data at end of text; copy it. */
while (dst < _edata) {
  *dst++ = *src++;
}

/* Zero bss */
for (dst = _bstart; dst< _bend; dst++)
  *dst = 0;
>region
이 섹션을 이전에 정의된 메모리 영역으로 할당한다. See section 메모리 레이아웃(Memory Layout).
:phdr
이 섹션을 프로그램 헤더에 의해서 기술된 세그먼트에 할당한다. See section ELF 프로그램 헤더(ELF Program Headers). 어떤 섹션이 하나 이상의 세그먼트들에 할당되면, 그들이 명시적으로 :phdr 변경자를 사용하지 않는 한, 모든 후속 할당된 섹션들도 같이 이런 세그먼트들로 할당될 것이다. 어떤 섹션이 어떤 세그먼트로 할당되는 것을 막으려면, 보통은 디폴트로 어떤 세그먼트에 할당될 때, :NONE를 사용하자.
=fill
=fill를 섹션 정의안에 포함하는 것은 초지정하는 것이다. fill를 지정하기 위해서 임의의 표현식을 사용할 수 있다. 현재 출력 섹션에 있는 임의의 할당되지 않은 구멍(hole)들은, 출력 파일에 기록될 때, 필요하다면 반복해서, 그 값의 LSB(least significant bytes)로 채워질 것이다. 또한 섹션 정의의 contentsFILL 문장을 써서 값을 채우도록 변경할 수 있다.

오버레이(Overlays)

OVERLAY 명령은 단일 메모리 이미지로써 로드되어야 하지만 동일한 메모리 주소에서 실행되어야 하는 섹션을 기술하는 쉬운 방법을 제공한다. 실행시에 어떤 종류의 오버레이 관리자는 오버레이된 섹션들을 요구된 실시간 메모리 주소로 그리고 이것으로부터 복사할 것이다. 아마 단순하게 어드레싱 비트들을 조작함으로써 그렇게 한다. 이런 접근은, 예를 들어서 메모리의 어떤 영역이 다른 것보다 더 빠를 때 유용할 수 있다.

OVERLAY 명령은 SECTIONS 명령에서 사용된다. 이것은 다음처럼 보인다:

  OVERLAY start : [ NOCROSSREFS ] AT ( ldaddr )
   {
     secname1 { contents } :phdr =fill
     secname2 { contents } :phdr =fill
     ...
   } >region :phdr =fill

모든 것은 OVERLAY (키워드) 를 제외하고 옵션이고 각 섹션은 반드시 이름 (위에서 secname1secname2) 을 가져야 한다. OVERLAY 구조안의 섹션 정의들은 일반적인 SECTIONS 구조안의 그것들과 동일하다 (see section 출력 섹션 지정(Specifying Output Sections)). 단 어떤 주소들도 그리고 어떤 메모리 영역들도 OVERLAY안에 있는 섹션들에 대해서 정의될 수 없다는 것을 제외하고.

섹션들은 모두 동일한 시작 주소로 정의된다. 섹션들의 로딩 주소들은 그들이 OVERLAY에 대해서 사용된 로드 주소에서 시작해서 메모리에 연속적으로 배치되도록 정렬된다(일반적인 섹션 정의에서처럼 로딩 주소는 옵션이고 디폴트는 시작 주소이다; 시작 주소도 또한 옵션이고 디폴트는 .이다).

NOCROSSREFS 키워드가 사용된다면 그리고 섹션들 사이에 다른 참조들이 있다면 링커는 에러를 보고할 것이다. 섹션들 모두가 동일한 주소에서 실행되기 때문에 하나의 섹션이 다른 것을 직접 참조하는 것은 일반적으로 의미가 없다. See section 옵션 명령(Option Commands).

OVERLAY 에 있는 각 섹션에 대해서 링커는 자동으로 두 개의 심벌들을 정의한다. 심벌 __load_start_secname는 그 섹션의 시작 로드 주소로써 정의된다. 심벌 __load_stop_secname는 그 섹션의 마지막 로드 주소로써 정의된다. secname안에 있는 C identifier들 안에서는 유효하지 않는, 임의의 문자들은 모두 제거된다. C (또는 어셈블러) 코드는 이런 심벌들을 필요한 대로 오버레이된 섹션들을 이동하는 데 사용할 수 있다.

오버레이 마지막에서 .의 값은 오버레이의 시작 주소에 가장 큰 섹션의 크기를 더한 것으로 설정된다.

다음은 예제이다. 이것은 SECTIONS 구조안에서 나타날 것이다라는 것을 기억하자.

  OVERLAY 0x1000 : AT (0x4000)
   {
     .text0 { o1/*.o(.text) }
     .text1 { o2/*.o(.text) }
   }

이것은 .text0.text1 둘다 0x1000 주소에서 시작하도록 정의할 것이다. .text0는 주소 0x4000에 로드될 것이고 .text1.text0 바로 뒤로 로드될 것이다. 다음 기호들이 정의될 것이다: __load_start_text0, __load_stop_text0, __load_start_text1, __load_stop_text1.

오버레이 .text1를 오버레이 영역안으로 복사하는 C 코드는 다음과 같이 보일 수 있다.

  extern char __load_start_text1, __load_stop_text1;
  memcpy ((char *) 0x1000, &__load_start_text1,
          &__load_stop_text1 - &__load_start_text1);

OVERLAY 명령은 단지 문법적인 설탕(sugar)이다. 왜냐면 이것이 하는 모든 일들이 좀 더 기본적인 명령들을 사용ㅎ서 이루어질 수 있기 때문이다. 위의 예는 다음과 같은 것으로 동일하게 작성될 수 있다.

  .text0 0x1000 : AT (0x4000) { o1/*.o(.text) }
  __load_start_text0 = LOADADDR (.text0);
  __load_stop_text0 = LOADADDR (.text0) + SIZEOF (.text0);
  .text1 0x1000 : AT (0x4000 + SIZEOF (.text0)) { o2/*.o(.text) }
  __load_start_text1 = LOADADDR (.text1);
  __load_stop_text1 = LOADADDR (.text1) + SIZEOF (.text1);
  . = 0x1000 + MAX (SIZEOF (.text0), SIZEOF (.text1));

ELF 프로그램 헤더(ELF Program Headers)

ELF 오브젝트 파일 포멧은 프로그램 헤더(program headers) 를 사용한다. 이것은 시스템 로더에 의해서 읽히고, 프로그램이 메모리로 적재되는 방법을 기술한다. 프로그램 헤더는 프로그램을 ELF 시스템에서 실행할 수 있도록 정확하게 설정되어야 한다. 링커는 타당한 프로그램 헤더들을 디폴트로 생성할 것이다. 그러나 어떤 경우에는 그 프로그램 헤더들을 좀 더 자세하게 명시하는 것이 바람직하다; PHDRS 명령은 이런 목적으로 사용될 수 있다. PHDRS 명령이 사용되면 링커는 어떤 프로그램 헤더들도 스스로 생성하지 않을 것이다.

PHDRS 명령은 단지 ELF 출력 파일을 생성할 때만 의미가 있다. 이것은 다른 경우에는 무시된다. 이 매뉴얼은 시스템 로더가 프로그램 헤더들을 해석하는 자세한 사항들을 기술한다; 좀 더 자세한 정보들을 보려면 ELF ABI를 보자. ELF 파일의 프로그램 헤더들은 objdump 명령의 `-p' 옵션을 사용해서 디스플레이될 수 있다.

다음은 PHDRS 명령의 문법이다. PHDRS, FILEHDR, AT, 그리고 FLAGS 단어들은 키워드들이다.

PHDRS
{
  name type [ FILEHDR ] [ PHDRS ] [ AT ( address ) ]
        [ FLAGS ( flags ) ] ;
}

name는 링커 스크립트의 SECTIONS 명령에서 참조를 위해서만 사용된다. 이것은 출력 파일에 들어가지 않는다.

어떤 프로그램 헤더 타입들은 파일로부터 시스템 로더에 의해서 로드되는 메모리 세그먼트들을 기술한다. 링커 스크립트에서 이런 세그먼트들의 내용물은 할당된 출력 섹션들이 그 세그먼트 안에 들어가도록(역자주: 리다이렉트) 함으로써 지정된다. 이렇게 하기 위해서 SECTIONS 명령에서 출력 섹션을 기술하는 명령은 `:name'를 사용해야 한다. 여기서 `:name'PHDRS 명령에 나타난 것과 같이 프로그램 헤더의 이름이다. See section 옵션인 섹션 속성(Optional Section Attributes).

어떤 섹션들이 하나 이상의 세그먼트 안에서 나타나는 것은 일반적이다. 이것은 `:name'를 반복함으로써 지정된다. 섹션이 나타날 프로그램 헤더 각각에 대해서 한번씩 이것을 사용하는 식으로 반복한다.

어떤 섹션이 `:name'를 사용하여 하나 이상의 세그먼트들안에 놓인다면, `:name'를 지정하지 않은 모든 후속 할당된 섹션들은 동일한 세그먼트들 안에 놓인다. 이것은 편의를 위한 것이다. 왜냐면 일반적으로 연속된 섹션들의 전체 집합은 단일 세그먼트 안에 놓일 것이기 때문이다. 디폴트로 하나에 할당되는 것이 관례이지만, 어떤 섹션이 하나의 세그먼트에 할당되는 것을 방지하려면, :NONE를 사용하자.

프로그램 헤더 타입 뒤에 나타날 수 있는 FILEHDRPHDRS 키워드들은 또한 메모리의 세그먼트 내용들을 가리킨다. FILEHDR 키워드는 그 세그먼트가 ELF 파일 헤더를 포함해야 한다는 것을 의미한다. PHDRS 키워드는 그 세그먼트가 ELF 프로그램 헤더 자신들을 포함해야 한다는 것을 의미한다.

type는 다음과 같은 것들 중의 하나일 수 있다. 숫자들은 키워드의 값을 나타낸다.

PT_NULL (0)
사용되지 않는 프로그램 헤더를 가리킨다.
PT_LOAD (1)
이 프로그램 헤더가 파일로부터 로드되는 세그먼트를 기술한다는 것을 가리킨다.
PT_DYNAMIC (2)
동적 링크 정보를 찾을 수 있는 세그먼트를 가리킨다.
PT_INTERP (3)
프로그램 해석기(interpreter)의 이름을 를 찾을 수 있는 세그먼트를 가리킨다.
PT_NOTE (4)
참고(note) 정보를 갖고 있는 세그먼트를 가리킨다.
PT_SHLIB (5)
예약된 헤더 타입. 정의된 것이지만 ELF ABI에 의해서 지정되지 않은 것.
PT_PHDR (6)
프로그램 헤더들을 찾을 수 있는 세그먼트를 가리킨다.
expression
프로그램 헤더의 숫자 타입을 제공하는 표현식. 이것은 위에서 정의되지 않은 타입들에 대해서 사용될 수 있다.

어떤 세그먼트가 메모리의 특정 주소에서 로드되어야 한다는 것을 지정하는 것은 가능하다. 이것은 AT 표현식을 사용해서 가능하다. 이것은SECTIONS 명령안에서 사용된 AT 명령에 동일하다 (see section 옵션인 섹션 속성(Optional Section Attributes)). 프로그램 헤더에 AT를 사용하는 것은 SECTIONS 명령에 있는 임의의 정보를 오버라이드한다.

일반적으로 세그먼트 플래그들은 섹션들에 기초해서 설정된다. FLAGS 키워드는 명시적으로 세그먼트 플래그들을 지정하는데 사용될 수 있다. flags의 값은 정수이어야 한다. 이것은 프로그램 헤더의 p_flags를 지정하는 데 사용될 수 있다.

다음은 PHDRS 사용의 예이다. 이것은 원(native) ELF 시스템에서 사용되는 프로그램 헤더들의 전형적인 모습을 보여준다.

PHDRS
{
  headers PT_PHDR PHDRS ;
  interp PT_INTERP ;
  text PT_LOAD FILEHDR PHDRS ;
  data PT_LOAD ;
  dynamic PT_DYNAMIC ;
}

SECTIONS
{
  . = SIZEOF_HEADERS;
  .interp : { *(.interp) } :text :interp
  .text : { *(.text) } :text
  .rodata : { *(.rodata) } /* defaults to :text */
  ...
  . = . + 0x1000; /* move to a new page in memory */
  .data : { *(.data) } :data
  .dynamic : { *(.dynamic) } :data :dynamic
  ...
}

엔트리 포인트(The Entry Point)

링커 명령 언어는 결과 파일에서 첫번째로 실행될 명령(entry point)을 정의하기 위해서 특별히 고안된 명령을 포함한다. 이것의 매개변수는 심벌 이름이다:

ENTRY(symbol)

심벌 할당처럼, ENTRY 명령은 명령 파일 안에서 독립 명령으로써 또는 SECTIONS 명령안에 있는 섹션 정의들 중에 있을 수 있다---어떤 것이든 레이아웃에 대해서 가장 의미있으면 된다.

ENTRY 는 엔트리 포인트를 선택하는 여러가지 방법들 중에서 유일한 것이다. 여러분은 이것을 다음 방법들 중의 하나로 지정할 수 있다(우선순위 내림차순으로 보여진: 이 리스트에서 좀 더 높은 방법들은 더 아래에 있는 방법들을 오버라이드한다).

  • `-e' entry 명령-라인 옵션:
  • 링커 제어 스크립트의 ENTRY(symbol) 명령:
  • 존재한다면 start 심벌의 값;
  • 존재한다면 .text 섹션의 첫번째 바이트 주소;
  • 주소 0.

예를 들어서 여러분은 이런 규칙들을 사용해서 할당 문장 안에서 엔트리 포인트를 생성할 수 있다: 입력 파일 안에 start 심벌이 정의되지 않았다면 이것에 적장한 값을 할당함으로써 정의할 수 있다---

start = 0x2020;

이 예제는 절대 주소를 보여주지만 임의의 표현식을 사용할 수 있다. 예를 들어서 여러분의 입력 오브젝트 파일들이 엔트리 포인트에 대해서 어떤 다른 심벌-이름 관례를 사용한다면 여러분은 start에 대한 시작 주소를 담고 있는 심벌이면 무엇이든 이것의 값을 할당할 수 있다:

start = other_symbol ;

버전 스크립트(Version Script)

링커 명령 스크립트는 특별히 버전 스크립트를 지정하기 위한 명령을 포함하며 공유 라이브러리들을 지원하는 ELF 플랫폼들에서만 의미가 있다. 버전 스크립트는 링크하는 시간에 링커에 대한 다른 입력 파일처럼, 여러분이 사용하는 링커 스크립트안에 직접 만들어 넣어질 수 있다. 명령 스크립트 문법은 다음과 같다:

VERSION { version script contents }

버전 스크립트는 `--version-script' 링커 명령행 옵션을 사용해서 링커에게 지정될 수 잇따. 버전 스크립트들은 공유 라이브러리들을 생성할 때만 의미가 있다.

버전 스크립트의 포멧은 솔라리스 2.5에서 Sun의 링커에 의해서 사용되는 그것과 동일하다. 버전을 매기는 것(versioning)은 이름을 가진 버전 노드들 트리와 버전 스크립트안에서 지정한 상호 의존성을 정의함으로써 수행된다. 버전 스크립트는 어떤 심벌들이 어떤 버전 노드에 의존하는지 지정할 수 있고 지정된 심벌 집합을 로컬 범위로 축소해서 그들이 공유 라이브러리 외부에서 글로벌하게 보이지 않도록 할 수 있다.

버전 스크립트 언어의 데모를 보여주는 가장 쉬운 방법은 다음과 같은 몇가지 예제들을 보는 것이다.

VERS_1.1 {
	 global:
		 foo1;
	 local:
		 old*; 
		 original*; 
		 new*; 
};

VERS_1.2 {
		 foo2;
} VERS_1.1;

VERS_2.0 {
		 bar1; bar2;
} VERS_1.2;

이 예제에서 세 버전 노드들이 정의되어 있다. `VERS_1.1'은 정의된 첫번째 버전 노드이고 다른 종속물들을 가지지 않는다. `foo1'는 버전 노드에 종속적이고 여러 오브젝트 파일들안에 보이는 많은 심벌들은 그 범위가 로컬로 줄어들어서 그들은 공유 메모리 바깥에서 보이지 않는다.

다음으로 `VERS_1.2'가 정의되어 있다. 이것은 `VERS_1.1'에 종속적이다. `foo2' 심벌은 이 버전 노드에 종속적이다.

마지막으로 `VERS_2.0' 노드가 정의된다. 이것은 `VERS_1.2'에 종속적이다. `bar1'`bar2' 심벌들은 이 버전 노드에 종속적이다.

어떤 버전 노드에 종속적이지 않는, 라이브러리내에서 정의된 심벌들은 그 라이브러리의 지정되지 않은 베이스 버전에 효과적으로 종속적이다. 모든 그렇지 않은 지정되지 않은 심벌들을 버전 스크립트 어딘가에 `global: *'를 사용한 주어진 버전 노드에 종속적이게 하는 것이 가능하다.

어휘적으로 버전 노드들의 이름은 그들이 그것을 읽는 사람에게 제시할 의미와 다른 구체적인 의미를 가지지 않는다. `2.0' 버전은 `1.1'`1.2' 사이에서 나타날 수도 있다. 그러나 이것은 버전 스크립트를 혼란스럽게 작성하는 방법이 될 것이다.

어플리케이션과 버전이 붙은 심벌(versioned symbol)을 가지는 공유 라이브러리를 링크할 때 어플리케이션 자신은 이것이 필요로 하는 각 심벌의 버전이 무엇인지를 알고 있으며, 링크하는 각 공유 라이브러리로부터 필요한 버전 노드가 무엇인가도 알고 있다. 그래서 실행시 동적 로더는 링크한 라이브러리들이 실제로 어플리케이션이 모든 동적 심벌들을 해독(resolve)해야 하는 모든 버전 노드들을 제공하는지 빨리 검사할 수 있다. 이런식으로 동적 로드는 이것이 필요로하는 모든 외부 심벌들이 각 심벌 참조에 대해서 조사를 할 필요없이 해독가능(resolvable)일 것이라고 생각하는 것이 가능하다.

심벌의 버전을 매기는 것(symbol versioning)은 SunOS가 하는 마이너 버전 검사(minor version checking)를 수행하는, 좀 더 복잡한 방법이다. 여기에 기술된 기본적인 문제는, 때 전형적으로 외부 함수들에 대한 참조가 필요한 바에 따르는 기초(as-needed basis)에 기반하고, 어플리케이션이 시작할 때 모두가 종속적이지는 않는다는 것이다. 공유 라이브러리가 오래된 것이라면 요구된 인터페이스가 없을 수 있다; 어플리케이션이 그 인터페이스를 사용하려고 할 때, 갑자기 예측하지 못한 실패를 겪을 것이다. 심벌에 버전을 매기는 것으로 사용자는, 사용된 라이브러리들이 너무 오래된 것이라면 그들의 프로그램을 시작할 때 경고를 얻을 것이다.

Sun의 버전 매기는 접근법에 대한 몇가지 GNU 확장판들이 있다. 이들중 첫번째는 심벌을 버전 스크립트 대신 심벌이 정의된 소스 파일에서 버전 노드에 바인딩하는 능력이다. 이것은 주로 라이브러리 관리자의 짐을 덜어주기 위해서 만들어졌다. 이것은 다음과 같은 것을 C 소스 파일안에 넣어서 이루어질 수 있다:

__asm__(".symver original_foo,foo@VERS_1.1");

이것은 함수 `original_foo'를 버전 노드 `VERS_1.1'에 바인딩된 `foo'에 대한 알리어스로 이름을 바꾸었다. `local:' 지시어는 심벌 `original_foo'이 익스포트되는 것을 막는 데 사용될 수 있다.

두번째 GNU 확장은 동일한 함수에 대한 여러 버전들이 주어진 공유 라이브러리안에 나타나도록 허락한다는 것이다. 이런식으로 어떤 인터페이스에 대한 비호환 변화는 공유 라이브러리의 주 버전 번호를 증가시키지 않고서 발생할 수 있다. 여전히 예전 인터페이스에 링크된 어플리케이션들이 계속 작동할 수 있도록 한다.

이것은 어셈블러 안에 다수의 `.symver' 지시어들을 사용함으로써만 성취될 수 있다. 이것의 예제는 다음과 같다:

__asm__(".symver original_foo,foo@");
__asm__(".symver old_foo,foo@VERS_1.1");
__asm__(".symver old_foo1,foo@VERS_1.2");
__asm__(".symver new_foo,foo@@VERS_2.0");

이 예제에서 `foo@'는 지정되지 않은 심벌의 베이스 버전에 묶인 `foo' 심벌을 나타낸다. 이 예제를 담고 잇는 소스 파일은 4개의 C 함수들을 정의할 것이다: `original_foo', `old_foo', `old_foo1', 그리고 `new_foo'.

주어진 심벌의 여러 정의들을 가지고 있다면 이 심벌에 대한 외부 참조들이 바인딩될 디폴트 버전을 제시하는 몇가지 방법들이 있어야 한다. 이것은 `.symver' 지시어의 `foo@@VERS_2.0' 타입으로 성취될 수 있다. 어떤 심벌의 한 버전만이 이런 식으로 '디폴트'로 선언될 수 있다 - 그렇지 않으면 동일한 심벌의 여러 정의들을 효과적으로 가지게 될 것이다.

공유 라이브러리안에 심벌의 특정 버전에다 참조를 바인딩하고자 한다면 여러분은 편리한 알리어스들을 사용할 수 있거나 (i.e. `old_foo'), 또는 `.symver' 지시어를 사용해서 명시적으로 문제의 함수 외부 버전에 바인딩할 수 있다.

옵션 명령(Option Commands)

명령 언어는 특별한 목적들에 사용할 수 있는 다른 여러 명령들을 포함한다. 그들은 명령행 옵션들과 목적상 비슷하다.

CONSTRUCTORS
a.out 오브젝트 파일 포멧을 사용하여 링크할 때 링커는 C++ 글로벌 생성자와 파괴자(destructor)를 지원하기 위해서 비일상적인 집합 구조를 사용한다. ECOFFXCOFF같은 임의 섹션을 지원하지 않는 오브젝트 파일 포멧을 링크할 때 링커는 자동으로 C++ 글로벌 생성자와 파괴자를 이름으로 인식한다. 이런 오브젝트 파일 포멧들에 대해서 CONSTRUCTORS 명려은 링커에게 이 정보가 놓여야 할 위치를 말한다. CONSTRUCTORS 명령은 다른 오브젝트 파일 포멧들에 대해서 무시된다. __CTOR_LIST__ 심벌은 글로벌 생성자의 시작점을 마킹하고__DTOR_LIST는 끝을 마킹한다. 리스트의 첫번째 단어는 엔트리들의 개수이고 그 뒤에 각 생성자나 파괴자의 주소가 따라오며 그 뒤에는 zero word가 따라 온다. 컴파일러는 반드시 그 코드를 실제로 실행하도록 정렬되어야 한다. 이런 오브젝트 파일 포멧들에 대해서 __main서브루틴으로부터 GNU C++가 생성자를 호출한다; __main에 대한 호출이 자동으로 main의 시작 코드안으로 삽입된다. GNU C++는atexit를 사용하거나 함수 exit로부터 직접적으로 파괴자들을 실행한다. 다수 섹션들을 지원하는 COFF 또는 ELF와 같은 오브젝트 파일 포멧들에 대해서 GNU C++는 일반적으로 글로벌 생성자와 파괴자의 주소들을 .ctors.dtors 섹션들안에 넣는다. 다음을 여러분의 링커 스크립트안에 넣는 것은 GNU C++ 실시간 코드가 보게될 것으로 기대하는 테이블 종류를 빌드할 것이다.
      __CTOR_LIST__ = .;
      LONG((__CTOR_END__ - __CTOR_LIST__) / 4 - 2)
      *(.ctors)
      LONG(0)
      __CTOR_END__ = .;
      __DTOR_LIST__ = .;
      LONG((__DTOR_END__ - __DTOR_LIST__) / 4 - 2)
      *(.dtors)
      LONG(0)
      __DTOR_END__ = .;
일반적으로 컴파일러와 링커는 이런 이슈들을 자동으로 처리할 것이고 여러분은 그것들에 관심을 가질 필요가 없을 것이다. 그러나 여러분은 C++을 사용하고 사용자 정의 링커 스크립를 작성하고 있다면 이것을 고려해야 할 것이다.
FLOAT
NOFLOAT
이런 키워드들은 어떤 더 오래된 링커들에서 특별한 수학 서브루틴 라이브러리를 요청하기 위해서 사용되었다. ld는 대신에 필요한 서브루틴들이 모두 아카이브로 링크하기 위한 일반 메카니즘들을 사용하여 지정된 라이브러리들안에 있다는 것을 가정하고, 이런 키워드들을 사용하지 않는다: 그러나 더 오래된 링커들에 대해서 쓰여진 스크립트의 사용을 허용하기 위해서 FLOATNOFLOAT 키워드들이 받아들여지지만 무시된다.
FORCE_COMMON_ALLOCATION
이 명령은 `-d' 명령행 옵션과 동일한 효과를 가진다: ld가 재배치 가능한 출력 파일이 지정되더라도 (`-r') 공용 심벌들에 공간을 ld가 할당하도록 하기 위해서 사용한다.
INCLUDE filename
링커 스크립트 filename를 이 시점에 포함한다. 그 파일은 현재 디렉터리에서, 그리고 -L 옵션으로 주어진 임의의 디렉터리에서 검색될 것이다. 여러분은 INCLUDE에 대한 호출을 10단계까지 겹칠 수 있다.
INPUT ( file, file, ... )
INPUT ( file file ... )
이 명령을 사용해서, 그들을 특별한 섹션 정의에 포함하지 않고서, 바이너리 입력 파일들을 링크에 포함한다. 각 file에 대한 완전한 이름을 지정하자. 필요하다면 `.a'를 포함해서. ld는 각 file을, 명령행에서 지정한 파일들에 대해서 하는 것과 동일하게, 아카이브-라이브러리 검색 경로에서 찾는다. `-L'에 대한 설명을 section 명령행 옵션들(Command Line Options)에서 보자. `-lfile'를 사용한다면 ldlibfile.a의 이름을 명령행 매개변수 `-l'에서와 같이 변환할 것이다.
GROUP ( file, file, ... )
GROUP ( file file ... )
이 명령은 INPUT와 비슷하다. 이름있는 파일들은 모두 아카이브들이어야 한다는 것과 그들이 새로운 정의되지 않은 참조들이 생성되지 않을 때까지 반복해서 검색된다는 것을 제외하고 비슷하다. `-('에 대한 설명은 section 명령행 옵션들(Command Line Options)를 보자.
OUTPUT ( filename )
이 명령은 링크 출력 파일의이름을 filename.로 지정할 때 사용한다. OUTPUT(filename)의 효과는, 이것을 오버라이드하는 `-o filename'의 효과와 동일하다. 여러분은 이 명령을 사용해서 a.out이 아닌 디폴트 출력-파일 이름을 지정할 수 있다.
OUTPUT_ARCH ( bfdname )
BFD 백-엔드 루틴들(see section BFD)에 의해서 사용되는 이름들 중의 하나로, 특별한 출력 머쉰 아키텍쳐를 지정한다. 이 명령은 종종 불필요하다; 아키텍쳐는 종종 시스템 BFD 설정이나 OUTPUT_FORMAT 명령의 부대 효과로써 암묵적으로 설정된다.
OUTPUT_FORMAT ( bfdname )
ld가 다수의 오브젝트 코드 포멧들을 지원하도록 설정될 때 여러분은 이 명령을 사용해서 특별한 출력 포멧을 지정할 수 있다.bfdname는 BFD 백-엔드 루틴(see section BFD)들에 의해서 사용되는 이름들 중의 하나이다. `--oformat' 명령행 옵션의 효과와 동일하다. 이 선택은 출력 파일에만 영향을 미친다; 관련된 명령 TARGET는 주로 입력 파일들에 영향을 미친다.
SEARCH_DIR ( path )
pathld가 아카이브 라이브러리들을 찾는 경로 리스트에 추가한다. SEARCH_DIR(path)는 명령행의 `-Lpath'과 동일한 효과를 가진다.
STARTUP ( filename )
filename이 링크 과정에서 사용된 첫번째 입력 파일이도록 한다.
TARGET ( format )
ld가 다수의 오브젝트 코드 포멧들을 지원하도록 설정될 때, 이 명령을 사용해서 입력-파일 오브젝트 포멧을 변경할 수 있다(명령-행 옵션 `-b'나 또는 유사어 `--format'를 사용하는 것과 같이). format 매개변수는 BFD에 의해서 바이너리 포멧들의 이름을 지정하는 데 사용되는 문자열들 중의 하나이다. TARGET이 지정되지만 OUTPUT_FORMAT은 아니라면 마지막 TARGET이 또한 ld 출력 파일에 대한 디폴트 포멧으로써 사용된다. See section BFD. TARGET 명령을 사용하지 않는다면 ld는 출력 파일 포멧을 선택하기 위해서 GNUTARGET 환경변수의 값을, 이것이 가능하다면, 사용한다. 그 변수도 없다면 ld는 BFD 라이브러리들안에서 여러분의 머쉰을 위해서 설정된 디폴트 포멧을 사용한다.
NOCROSSREFS ( section section ... )
이 명령은 ld에게 어떤 섹션들 사이에 있는 참조들에 대해서 에러를 발생하도록 하라고 지시하는 데 사용될 수 있다. 어떤 프로그램 타입들에서는, 특별히 임베디드 시스템들에서는, 한 섹션이 메모리로 로드될 때 다른 섹션은 그렇지 않을 것이다. 두 섹션들 사이의 직접적인 참조들은 어떤 것이든 에러를 발생할 것이다. 예를 들어서 한 섹션에 있는 코드가 다른 섹션에 있는 함수를 호출했다면 에러가 발생할 것이다. NOCROSSREFS 명령은 섹션 이름들 리스트를 취한다. ld가 이런 섹션들 사이의 교차 참조를 하나라도 검출한다면 이것은 에러를 보고하고 0이 아닌 종료 상태값을 리턴한다. NOCROSSREFS 명령은 SECTIONS 명령에서 정의된 출력 섹션 이름들을 사용한다. 이것은 입력 섹션들의 이름은 사용하지 않는다.
크리에이티브 커먼즈 라이센스
Creative Commons License
2012/04/18 14:58 2012/04/18 14:58
받은 트랙백이 없고, 댓글이 없습니다.

댓글+트랙백 RSS :: http://blog.hwport.com/rss/response/656

댓글+트랙백 ATOM :: http://blog.hwport.com/atom/response/656


 본 내용은 "http://wiki.kldp.org/wiki.php/ShellProgrammingTutorial?action=print" 에서 발췌한 것임을 밝힙니다.

----



쉘 프로그래밍 강좌

  • 참고서적: 초보자용 리눅스 프로그래밍 (대림출판사, 한동훈,이만용역, NEIL MATTHEW, RICHARD STONES 저)
  • 넷츠고 리눅스 동호회 7월 제 5회 정기 공개강좌 자료
  • 글쓴이: 위경섭 <powerhack@netsgo.com>
  • 위키문서 변환: 윤현호 <hhyoon@kldp.org> 2005년 2월 23일. 원본: http://wiki.kldp.org/KoreanDoc/Shell_Programming-KLDP 


1 변수

  • 쉘변수는 처음 사용될 때 만들어진다. 즉 미리 선언할 필요가 없다.
  • 쉘변수는 유닉스 명령과 마찬가지로 대소문자에 구별이 있다.
  • 쉘변수는 기본적으로 데이터를 문자열로 저장한다. 수치를 대입해도 실제 수치가 아닌 문자열이 저장된다. 계산이 필요할 경우는 자동으로 수치로 변환하여 계산후 다시 문자열로 저장된다.
  • 쉘변수의 값을 사용할 때는 변수명앞에 "$" 를 붙여서 사용한다.
  • 쉘변수에 값을 대입할때는 "$"를 사용하지 않는다.
  • 쉘변수는 타입이 없다. 즉 아무 값이나 다 넣을 수 있다.

1.1 환경변수

쉘을 기동하고나면 기본적으로 셋팅되어있는 변수들이다. 유닉스/리눅스에는 많은 환경변수들이 있고 필요한경우 이 변수들을 마치 일반변수처럼 값을 얻어오거나 셋팅할 수 있다. 여기서는 쉘과 직접적인 관련이 있는것만 설명한다.
  • $0 - 실행된 쉘 스크립트 이름
  • $# - 스크립트에 넘겨진 인자의 갯수
  • $$ - 쉘 스크립트의 프로세스 ID

1.2 인자 변수

쉘스크립트에 인자를 넘겨줄때 그 인자들에 대한 정보를 가지고 있는 변수들.
  • $1~ $nnn - 넘겨진 인자들
  • $* - 스크립트에 전달된 인자들을 모아놓은 문자열. 하나의 변수에 저장되며 IFS 환경변수의 첫번째 문자로 구분된다.
  • $@ - $*과 같다. 다만 구분자가 IFS변수의 영향을 받지 않는다.

2 일반변수

일반변수에 특별한 제약은 없다. 단 대소문자 구분만 정확하게 해주면 된다.
예제
#!/bin/sh
echo "This Script Executable File : $0"
echo "Argument Count : $#"
echo "Process ID : $$"
echo "Argument List \$* : $*"
echo "Argument List \$@ : $@"
echo "Argument 1 : $1"
echo "Argument 2 : $2"
echo "Argument 3 : $3"
echo "Argument 4 : $4"  
실행
$chmod 755 test1
$./test1 a1 a2 a3 a4
This Script Executable File : ./test1
Argument Count : 4
Process ID : 905
Argument List $* : a1 a2 a3 a4
Argument List $@ : a1 a2 a3 a4
Argument 1 : a1
Argument 2 : a2
Argument 3 : a3
Argument 4 : a4

2.1 연산

변수의 산술 연산은 생각하는 것처럼 쉽지 않다. 위에서 언급했듯이 변수에는 모든 것이 문자열로 저장되기 때문에 연산이 불가능하다. 연산을 위해서는 좀 복잡한 절차를 거쳐야 한다.
변수 = $((산술식))
이것이 가장 단순한 연산 규칙이다. 산술식내에는 변수($1, $a 와 같은) 도 들어갈 수 있다. 산술식 내에 숫자가 아닌 문자열, 또는 문자열이 담겨있는 변수가 들어가면 그것들은 계산에서 제외된다. (정확히 말하면 0 으로 간주되어 연산이 이루어 지지 않는다.)

2.2 매개변수 확장

매개변수 확장이란 변수의 값을 문자열 등으로 대체하는 것을 말한다. 단순한 대체뿐 아니라 변수내의 문자열을 조작하여 원하는 문자열만을 추출할 수도 있다.

형식:
  • ${parm:-default} - parm이 존재하지 않으면 default로 대체된다.
  • ${#parm} - parm의 길이를 참조한다.(가져온다)
  • ${parm%word} - 끝에서부터 word와 일치하는 parm의 최소부분(첫번째 일치)을 제거하고 나머지를 반환한다.
  • ${parm%%word} - 끝에서부터 word와 일치하는 parm의 최대부분(마지막 일치)을 제거하고 나머지를 반환한다.
  • ${parm#word} - 처음부터 word와 맞는 parm의 최소부분(첫번째 일치)을 제거하고 나머지 부분을 반환한다.
  • ${parm##word} - 처음부터 word와 맞는 parm의 최대부분(마지막 일치)을 제거하고 나머지를 반환한다.
word에는 와일드 카드를 사용할 수 있다.예를 보자.
1 #!/bin/sh
2
3 p="/usr/X11R6/bin/startx"
4
5 unset p
6 a=${p:-"Variable p Not found"}
7 echo $a
8
9 p="/usr/X11R6/bin/startx"
10 a=${p:-"Variable parm Not found"}
11 echo $a
12
13 a=${#p}
14 echo $a
15
16 a=${p%/*}
17 echo $a
18
19 a=${p%%/*}
20 echo $a
21
22 a=${p#*/}
23 echo $a
24
25 a=${p##*/}
26 echo $a
27                    
위 스크립트의 결과는 다음과 같다.
Variable p Not found
/usr/X11R6/bin/startx
21
/usr/X11R6/bin

usr/X11R6/bin/startx
startx
  • 6행 : 변수 p 가 제거 되었으므로 "Variable p Not found" 가 a에 들어간다.
  • 10행 : 변수 p 가 있으므로 그대로 a에 들어간다.
  • 13행 : a에는 변수 p의 길이가 들어간다.
  • 16행 : p 에서 가장 오른쪽의 "/"부터 끝까지 지우고 나머지를 a에 넣는다.
  • 19행 : p 에서 가장 왼쪽의 "/" 부터 끝까지 지우고 나머지를 a에 넣는다. (아무것도 없다)
  • 22행 : p 의 처음부터 가장왼쪽의 "/" 까지 지우고 나머지를 a에 넣는다.
  • 25행 : p 의 처음부터 가장 오른쪽의 "/"까지 지우고 나머지를 a에 넣는다.

3 조건 판단

쉘 스크립트에서 조건판단은 if 와 test 명령을 혼합하여 사용한다. 일반적인 예는 다음과 같다.
if test -f test1
then
...
fi


-f 는 주어진 인자가 일반 파일일 때 참이 된다.
test 명령은 [] 로 대체될 수 있다.
if [ -f test1 ]
then
...
fi

if [ -f test1 ]; then
...
fi

3.1 test 명령

test 명령의 조건은 다음과 같이 세 부류로 나누어진다.

3.1.1 문자열 비교

  • [ string ] - string이 빈 문자열이 아니라면 참
  • [ string1 = string2 ] - 두 문자열이 같다면 참
  • [ string1 != string2 ] - 두 문자열이 다르면 참
  • [ -n string ] - 문자열이 null(빈 문자열) 이 아니라면 참
  • [ -z string ] - 문자열이 null(빈 문자열) 이라면 참

3.1.2 산술 비교

  • [ expr1 -eq expr2 ] - 두 표현식 값이 같다면 참 ('EQual')
  • [ expr1 -ne expr2 ] - 두 표현식 값이 같지 않다면 참 ('Not Equal')
  • [ expr1 -gt expr2 ] - expr1 > expr2 이면 참 ('Greater Than')
  • [ expr1 -ge expr2 ] - expr1 >= expr2 이면 참 ('Greater Equal')
  • [ expr1 -lt expr2 ] - expr1 < expr2 이면 참 ('Less Than')
  • [ expr1 -le expr2 ] - expr1 <= expr2 이면 참 ('Less Equal')
  • [ ! expr ] - expr 이 참이면 거짓, 거짓이면 참
  • [ expr1 -a expr2 ] - expr1 AND expr2 의 결과 (둘다 참이면 참, 'And')
  • [ expr1 -o expr2 ] - expr1 OR expr2 의 결과 (둘중 하나만 참이면 참, 'Or')

3.1.3 파일 조건

  • [ -b FILE ] - FILE 이 블럭 디바이스 이면 참
  • [ -c FILE ] - FILE 이 문자 디바이스 이면 참.
  • [ -d FILE ] - FILE 이 디렉토리이면 참
  • [ -e FILE ] - FILE 이 존재하면 참
  • [ -f FILE ] - FILE 이 존재하고 정규파일이면 참
  • [ -g FILE ] - FILE 이 set-group-id 파일이면 참
  • [ -h FILE ] - FILE 이 심볼릭 링크이면 참
  • [ -L FILE ] - FILE 이 심볼릭 링크이면 참
  • [ -k FILE ] - FILE 이 Sticky bit 가 셋팅되어 있으면 참
  • [ -p FILE ] - True if file is a named pipe.
  • [ -r FILE ] - 현재 사용자가 읽을 수 있는 파일이면 참
  • [ -s FILE ] - 파일이 비어있지 않으면 참
  • [ -S FILE ] - 소켓 디바이스이면 참
  • [ -t FD ] - FD 가 열려진 터미널이면 참
  • [ -u FILE ] - FILE 이 set-user-id 파일이면 참
  • [ -w FILE ] - 현재 사용자가 쓸 수 있는 파일(writable file) 이면 참
  • [ -x FILE ] - 현재사용자가 실행할 수 있는 파일(Executable file) 이면 참
  • [ -O FILE ] - FILE 의 소유자가 현재 사용자이면 참
  • [ -G FILE ] - FILE 의 그룹이 현재 사용자의 그룹과 같으면 참
  • [ FILE1 -nt FILE2 ] - : FILE1FILE2 보다 새로운 파일이면 ( 최근파일이면 ) 참
  • [ FILE1 -ot FILE2 ] - : FILE1FILE2 보다 오래된 파일이면 참
  • [ FILE1 -ef FILE2 ] - : FILE1 이 FILE2의 하드링크 파일이면 참

3.2 if 구문

if 문은 조건을 판단하여 주어진 문장을 수행한다.

3.2.1 형식 1 (단일 if 문)

형식:
if [ 조건 ]
then
    문장1
    문장2
fi

3.2.2 형식 2 (if-else 문)

형식:
if [ 조건 ]
then
    문장3
    문장4
fi

3.2.3 형식 3 (if-elif 문)

형식:
if [ 조건 ]
then
    문장1
    문장2
elif
    문장3
    문장4
else
    문장5
    문장6
fi

3.3 case 구문

'패턴'에는 * 문자, 즉 와일드카드를 사용할 수 있다.
형식:
case 변수 in
패턴 [ | 패턴 ] ... ) 문장 ;;
패턴 [ | 패턴 ] ... ) 문장 ;;
....
* ) 문장 ;;
esac

3.4 목록

여러 명령을 실행할때 앞의 명령의 결과에 의해서 다음행동이 결정되어야 할 경우가 있다. 이런경우에 AND나 OR조건을 사용해서 한번에 처리할 수 있다. 이것은 쉘 스크립트 뿐 아니라 명령행에서도 사용 가능하다. 물론 if 문을 이용해서 반환값을 검사하여 처리할 수 있지만 문장이 길어지고 복잡해진다.

3.4.1 AND 목록

statment1 && statment2 && statmentN && .....


위의 명령들은 각 명령이 거짓이 될 때 까지 명령을 수행해 나간다. 수행 도중 결과가 거짓이 되면 그이후의 명령은 수행되지 않는다.

3.4.2 OR 목록

statment1 || statment2 || statmentN || .....


위의 명령들은 각 명령이 거짓이 나오는 동안 계속된다. 즉 참이 나오면 실행을 멈춘다.

3.4.3 AND와 OR목록은 혼용이 가능하다.

[ 조건 ] && 문장1 || 문장2


위의 예는 조건이 참이면 문장1을 수행하고 거짓이면 문장2를 수행한다.
또한 위의 문장1이나 문장2에서 여러개의 문장을 수행하고 싶을 때는 {}를 사용하면 된다.
[조건] && {
    문장1
    문장2
    문장3
} || {
    문장4
    문장5
    문장6
}

4 제어문


4.1 for

for 문은 지정된 범위안에서 루프를 수행한다. 범위는 어떤 집합도 가능하다.
형식:
for 변수 in 값1, 값2, ...
do
    문장
done


매 루프를 돌때마다 변수의 값은 in 이후의 값으로 대체된다.
예제:
for str in "test1", "test2", "test3", "test4"
do
    echo @str
done

출력:
test1
test2
test3
test4

값에는 와일드 카드 확장을 사용할 수 있다.
for file in $(ls -a | grep "^\.")
do
    echo "$file is Hidden File"
done


위 예의 출력 결과는 현재 디렉토리에서 처음이 "." 으로시작하는 파일(히든파일)만을 출력한다.
for file in $(ls chap[345].txt); do
    echo "--- $file ---" >> Books.txt
    cat $file >> Books.txt
done


위의 예는 chap3.txt, chap4.txt, chap5.txt 파일을 Books.txt 라는 파일에 붙여 넣는다.
다음의 예를 보고 결과를 예측해보자.
echo "\$* output"

for fvar in $*
do
    echo $fvar
done

echo "\$@ output"
for fvar in $@
do
    echo $fvar
done

4.2 while

for 명령의 경우는 횟수를 지정해서 루프를 수행하는 데는 문제가 있다. while 문은 실행 횟수가 지정되지 않았을 때 편리하다.
형식:
while 조건문
do
    문장
done

예제를 보자. 패스워드를 입력받고 맞는지 확인하는 프로그램이다.
echo "Enter Password : "
read password1

echo "Retype Password : "
read password2

while [ "$password1" != "$password2" ]
do
    echo "Password mismatch Try again "

    echo "Retype Password : "
    read password2
done

echo "OK Password Match complete"


어떻게 동작하는가 ?

4.3 until

until은 while문과 동일한 효과를 내지만 조건이 반대이다. 즉, while문은 조건이 참일동안 루프를 수행하지만 until은 조건이 거짓일 동안 루프를 수행한다.
형식:
until 조건문
do
    문장
done

다음 예를 보자. 이 예는 지정한 유저가 로그인하면 알려준다.
#!/bin/sh

until who | grep "$1" > /dev/null
do
    sleep 10
done

echo "User $1 just logged in ^_^"

4.4 select

select문은 원하는 리스트를 출력하고 그 중 선택된 것을 돌려주는 구문이다. 주의할 점은 select의 루프 내에서는 자동적으로 루프를 벗어날 수 없다. 반드시 break문을 사용해서 루프를 벗어나야 한다.
예: 간단한 퀴즈
#!/bin/sh

echo "다음중 스크립트언어 프로그래밍에 속하는 것은 ?"
select var in "쉘 프로그래밍" "C 프로그래밍" "자바 프로그래밍" "Exit"
do
    if [ "$var" = "쉘 프로그래밍" ]
    then
        echo "정답입니다."
        exit 0
    elif [ "$var" = "Exit" ]
    then
        echo "종료합니다."
        exit 1
    else
        echo "$var 을 선택하셨습니다. 오답입니다."
        echo "다음중 스크립트언어 프로그래밍에 속하는 것은 ?"
    fi
done

5 함수

쉘 스크립트 내부에 또는 다른 스크립트파일에 함수를 정의해 놓고 사용할 수 있다. 함수를 사용하면 코드를 최적화 할 수 있고, 코딩이 간결해지며,재사용이 가능하다. 그러나 다른 스크립트 파일을 호출해서 함수를 실행할 경우, 가능은 하지만 스크립트의 실행시간이 길어지고, 함수의 결과를 전달하는 것이 까다롭기 때문에 가급적이면 외부파일의 함수는 안쓰는 것이 좋다.
형식:
함수명 ()
{
	문장
	return 값
}

사용
함수명 인자1, 인자2, ...


함수는 독립적으로 $#, $*, $0 등의 인자 변수를 사용한다. 즉 함수내의 $#과 본체의 $#은 다를 수 있다는 것이다.
다음의 예를 보자
#!/bin/sh
		
func()
{
    echo ------ this is func --------
    echo "This Script Executable File : $0"
    echo "Argument Count : $#"
    echo "Process ID : $$"
    echo "Argument List \$* : $*"
    echo "Argument List \$@ : $@"
    echo "Argument 1 : $1"
    echo "Argument 2 : $2"
    echo "Argument 3 : $3"
}

echo ------ this is main --------
echo "This Script Executable File : $0"
echo "Argument Count : $#"
echo "Process ID : $$"
echo "Argument List \$* : $*"
echo "Argument List \$@ : $@"
echo "Argument 1 : $1"
echo "Argument 2 : $2"
echo "Argument 3 : $3"
echo "Argument 4 : $4"
func aa bb cc 


본체와 함수에서 동일한 변수를 보여주지만 값은 틀린다는 것을 알 수 있다.

함수에서 값을 반환하기 - 함수에서 반환값은 반드시 정수값만을 반환할 수 있다. 이 값을 if 등으로 조건을 판단해서 사용할 수 있다. 반환값 중 0은 참으로 나머지 숫자는 거짓으로 판별된다.

6 명령어

쉘에서 쓸 수 있는 명령어는 두가지로 나누어진다. 명령 프롬프트 상에서 실행 시킬 수 있는 외부 명령어와 쉘 내부 명령이다. 내부 명령은 보통 쉘 내부나 쉘 구문상에서 쓰인다. 외부명령은 쉘에 관계없이 사용이 가능하다.

6.1 break

제어문이나 조건문의 루프를 빠져나갈때 사용한다.
예제
while [ $a -eq 10 ]
do
    if [ $a -eq 5 ]; then
        break
    fi
done

6.2 continue

제어문이나 조건문의 처음으로 돌아가서 다시수행한다.
예제
while [ $a -eq 10 ]
do
    if [ $a -eq 5 ]; then
        continue
    fi
done

6.3 : 명령

의미없는 명령. 논리값 true를 대신해 쓰기도 한다.

6.4 . 명령

. 명령을 사용하면 현재 쉘에서 명령을 실행시킨다 그러므로 실행된 명령의 결과를 본 프로그램에서 사용할 수 있다.

예를 들면 A 라는 스크립트에서 B라는 스크립트를 그냥 실행할 경우 B에서의 변화(환경변수 등)는 A에게 아무런 영향도 미치지 않는다. 그러나 . 명령을 사용해서 실행하면 B에서의 변화가 A에도 영향을 미친다.

6.5 echo

문장을 출력한다. 자동으로 개행문자가 삽입된다. (다음 줄로 넘어간다)

6.6 eval

인자의 실제 값을 구하는데 사용한다.
foo=10
x=foo
y='$'$x
echo $y

이 예를 실행해 보면 $foo가 출력된다
foo=10
x=foo
eval y='$'$x
echo $y


이 예에서는 $foo의 값 즉 10 이 출력된다. eval명령은 원하는 문자열들을 조합해서 변수를 액세스 할 수 있다.

6.7 exec

현재 쉘을 다른 프로그램으로 대체한다.
예제
exec csh

6.8 exit n

현재 쉘을 종료한다. 종료시 n 값을 리턴한다.

6.9 export

해당 쉘에서 파생된 자식 프로세스에서 export한 환경변수는 본래 쉘에서 관리한다.

6.10 expr

표현식의 값을 구한다.
x=`expr 1 + 2`
요즘은 expr보다는 $((계산식)) 구문을 많이 사용한다.

6.11 printf

C 언어의 printf명령과 흡사하다.
형식:
printf "Format String" arg1 arg2 arg3 ...

6.12 return

쉘 함수에서 값을 반환 할 때 쓰인다. 0은 성공을 1~125까지는 쉘 에러코드를 나타낸다.

6.13 set

쉘 내부에서 매개 인자를 설정한다. set의 인자로 쓰인 문자열은 공백에 의해 $1 부터 차례대로 대입된다.
예제
#!/bin/sh
echo $#
set $(ls)
echo $# 

결과는
0
22


이다. (22는 필자의 ls 결과의 갯수이다.) 첫번째 0는 이 스크립트에 인수가 없으므로 0이고 set $(ls) 에 의해서 인수의 갯수가 22개로 늘었다.

6.14 shift

쉘의 인자를 한자리씩 아래로(n -> 1 로) 이동시킨다.
예제
#!/bin/sh

echo $1
shift
echo $1
shift 5
echo $1

실행
#./myscript 1 2 3 4 5 6 7 8 9 0
1
2
7

6.15 trap

쉘의 실행도중 시그널을 처리하는 시그널 처리기를 만드는 역할을 한다.
형식:
trap command signal


쉘 스크립트는 위에서 아래로 실행되므로 보호하려는 부분 이전에 trap 명령을 사용해야 한다. trap 조건을 기본으로 사용하려면 명령에 - 를 넣으면 된다. 신호를 무시하려면 '' 빈 문자열을 준다.

6.16 unset

변수나 함수를 제거한다.

7 명령 실행

외부 명령의 실행 결과를 변수에 집어넣어 변수의 값으로 사용할 수 있다.
형식:
x = $(명령)


이렇게 변수에 결과를 넣은 후에는 이 변수를 일반문자열로 생각하고 원하는 가공을 해서 결과를 얻어낼 수 있다. 위에서 보았던 매개변수 확장이나 set명령을 이용해서 원하는 부분을 추출해 내면 그만이다.

8 쉘 스크립트 내부에서 명령에 입력 전달하기 (Here Documents)

이 기능은 쉘 내부에서 명령어에 입력을 전달하는 방법이다. 전달된 입력은 마치 키보드에서 눌려진 것처럼 반응한다.
형식:
명령 << 종료문자열
입력값.....
종료문자열

예제: 자동으로 메일을 보내는 스크립트
#!/bin/sh

mail $1 << myscript
This is Header
This is Body
.

myscript

9 디버깅 하기

쉘 프로그래밍 시 간단하게 디버깅하는 방법을 소개합니다.

9.1 쉘 옵션

  • sh -n 스크립트 : 문법 에러만을 검사, 명령을 실행하지 않음
  • sh -v 스크립트 : 명령을 실행하기 전에 에코
  • sh -x 스크립트 : 명령줄에서 처리한 다음 에코

9.2 set 옵션

위의 쉘 옵션은 아래와 같이 set 옵션으로도 설정할 수 있다.
  • set -o noexec 또는 set -n : 문법 에러만을 검사, 명령을 실행하지 않음
  • set -o verbose 또는 set -v : 명령을 실행하기 전에 에코
  • set -o xtrace 또는 set -x : 명령줄에서 처리한 다음 에코
  • set -o nounset 또는 set -u : 정의되지 않은 변수가 사용되면 에러 메시지를 제공한다.
아래와 같이 set -x를 이용하여 손쉽게 실행과정을 추적할 수 있다. (참고로 set 옵션을 취소하려면 set +x를 입력하면 된다. 다른 옵션도 마찬가지)
set -x
for str in "test1" "test2" "test3" "test4"
do
    echo $str
done

결과
+ for str in '"test1"' '"test2"' '"test3"' '"test4"'
+ echo @str
@str
+ for str in '"test1"' '"test2"' '"test3"' '"test4"'
+ echo @str
@str
+ for str in '"test1"' '"test2"' '"test3"' '"test4"'
+ echo @str
@str
+ for str in '"test1"' '"test2"' '"test3"' '"test4"'
+ echo @str
@str
Retrieved from http://wiki.kldp.org/wiki.php/ShellProgrammingTutorial
last modified 2009-03-12 22:28:20
크리에이티브 커먼즈 라이센스
Creative Commons License
2012/04/17 18:01 2012/04/17 18:01
TAG , ,
받은 트랙백이 없고, 댓글이 없습니다.

댓글+트랙백 RSS :: http://blog.hwport.com/rss/response/655

댓글+트랙백 ATOM :: http://blog.hwport.com/atom/response/655


본 내용은 "http://www.gnu.org/software/libtool/manual/libtool.html"에서 발췌한 것임을 밝힙니다.


----



This file documents GNU Libtool 2.4.2

Copyright (C) 1996-2011 Free Software Foundation, Inc.

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".

Table of Contents


Next: , Previous: (dir), Up: (dir)

Shared library support for GNU

This file documents GNU Libtool, a script that allows package developers to provide generic shared library support. This edition documents version 2.4.2.

See Reporting bugs, for information on how to report problems with GNU Libtool.

--- The Detailed Node Listing ---

Introduction

Using libtool

Linking executables

Invoking libtool

Integrating libtool with your package

Configuring libtool

Including libtool in your package

Using libtool with other languages

Library interface versions

Tips for interface design

Dlopened modules

Using libltdl

Frequently Asked Questions about libtool

Troubleshooting

The libtool test suite

Maintenance notes for libtool

Porting libtool to new systems

Platform quirks

File name conversion


Next: , Previous: Top, Up: Top

1 Introduction

In the past, if you were a source code package developer and wanted to take advantage of the power of shared libraries, you needed to write custom support code for each platform on which your package ran. You also had to design a configuration interface so that the package installer could choose what sort of libraries were built.

GNU Libtool simplifies your job by encapsulating both the platform-specific dependencies, and the user interface, in a single script. GNU Libtool is designed so that the complete functionality of each host type is available via a generic interface, but nasty quirks are hidden from the programmer.

GNU Libtool's consistent interface is reassuring... users don't need to read obscure documentation in order to have their favorite source package build shared libraries. They just run your package configure script (or equivalent), and libtool does all the dirty work.

There are several examples throughout this document. All assume the same environment: we want to build a library, libhello, in a generic way.

libhello could be a shared library, a static library, or both... whatever is available on the host system, as long as libtool has been ported to it.

This chapter explains the original design philosophy of libtool. Feel free to skip to the next chapter, unless you are interested in history, or want to write code to extend libtool in a consistent way.


Next: , Up: Introduction

1.1 Motivation for writing libtool

Since early 1995, several different GNU developers have recognized the importance of having shared library support for their packages. The primary motivation for such a change is to encourage modularity and reuse of code (both conceptually and physically) in GNU programs.

Such a demand means that the way libraries are built in GNU packages needs to be general, to allow for any library type the package installer might want. The problem is compounded by the absence of a standard procedure for creating shared libraries on different platforms.

The following sections outline the major issues facing shared library support in GNU, and how shared library support could be standardized with libtool.

The following specifications were used in developing and evaluating this system:

  1. The system must be as elegant as possible.
  2. The system must be fully integrated with the GNU Autoconf and Automake utilities, so that it will be easy for GNU maintainers to use. However, the system must not require these tools, so that it can be used by non-GNU packages.
  3. Portability to other (non-GNU) architectures and tools is desirable.

1.2 Implementation issues

The following issues need to be addressed in any reusable shared library system, specifically libtool:

  1. The package installer should be able to control what sort of libraries are built.
  2. It can be tricky to run dynamically linked programs whose libraries have not yet been installed. LD_LIBRARY_PATH must be set properly (if it is supported), or programs fail to run.
  3. The system must operate consistently even on hosts that don't support shared libraries.
  4. The commands required to build shared libraries may differ wildly from host to host. These need to be determined at configure time in a consistent way.
  5. It is not always obvious with what prefix or suffix a shared library should be installed. This makes it difficult for Makefile rules, since they generally assume that file names are the same from host to host.
  6. The system needs a simple library version number abstraction, so that shared libraries can be upgraded in place. The programmer should be informed how to design the interfaces to the library to maximize binary compatibility.
  7. The install Makefile target should warn the package installer to set the proper environment variables (LD_LIBRARY_PATH or equivalent), or run ldconfig.

Next: , Previous: Issues, Up: Introduction

1.3 Other implementations

Even before libtool was developed, many free software packages built and installed their own shared libraries. At first, these packages were examined to avoid reinventing existing features.

Now it is clear that none of these packages have documented the details of shared library systems that libtool requires. So, other packages have been more or less abandoned as influences.

1.4 A postmortem analysis of other implementations

In all fairness, each of the implementations that were examined do the job that they were intended to do, for a number of different host systems. However, none of these solutions seem to function well as a generalized, reusable component.

Most were too complex to use (much less modify) without understanding exactly what the implementation does, and they were generally not documented.

The main difficulty is that different vendors have different views of what libraries are, and none of the packages that were examined seemed to be confident enough to settle on a single paradigm that just works.

Ideally, libtool would be a standard that would be implemented as series of extensions and modifications to existing library systems to make them work consistently. However, it is not an easy task to convince operating system developers to mend their evil ways, and people want to build shared libraries right now, even on buggy, broken, confused operating systems.

For this reason, libtool was designed as an independent shell script. It isolates the problems and inconsistencies in library building that plague Makefile writers by wrapping the compiler suite on different platforms with a consistent, powerful interface.

With luck, libtool will be useful to and used by the GNU community, and that the lessons that were learned in writing it will be taken up by designers of future library systems.


Next: , Previous: Introduction, Up: Top

2 The libtool paradigm

At first, libtool was designed to support an arbitrary number of library object types. After libtool was ported to more platforms, a new paradigm gradually developed for describing the relationship between libraries and programs.

In summary, “libraries are programs with multiple entry points, and more formally defined interfaces.”

Version 0.7 of libtool was a complete redesign and rewrite of libtool to reflect this new paradigm. So far, it has proved to be successful: libtool is simpler and more useful than before.

The best way to introduce the libtool paradigm is to contrast it with the paradigm of existing library systems, with examples from each. It is a new way of thinking, so it may take a little time to absorb, but when you understand it, the world becomes simpler.


Next: , Previous: Libtool paradigm, Up: Top

3 Using libtool

It makes little sense to talk about using libtool in your own packages until you have seen how it makes your life simpler. The examples in this chapter introduce the main features of libtool by comparing the standard library building procedure to libtool's operation on two different platforms:

a23
An Ultrix 4.2 platform with only static libraries.
burger
A NetBSD/i386 1.2 platform with shared libraries.

You can follow these examples on your own platform, using the preconfigured libtool script that was installed with libtool (seeConfiguring).

Source files for the following examples are taken from the demo subdirectory of the libtool distribution. Assume that we are building a library, libhello, out of the files foo.c and hello.c.

Note that the foo.c source file uses the cos math library function, which is usually found in the standalone math library, and not the C library (see Trigonometric Functions). So, we need to add -lm to the end of the link line whenever we link foo.lo into an executable or a library (see Inter-library dependencies).

The same rule applies whenever you use functions that don't appear in the standard C library... you need to add the appropriate -lnameflag to the end of the link line when you link against those objects.

After we have built that library, we want to create a program by linking main.o against libhello.

3.1 Creating object files

To create an object file from a source file, the compiler is invoked with the -c flag (and any other desired flags):

     burger$ gcc -g -O -c main.c
     burger$

The above compiler command produces an object file, usually named main.o, from the source file main.c.

For most library systems, creating object files that become part of a static library is as simple as creating object files that are linked to form an executable:

     burger$ gcc -g -O -c foo.c
     burger$ gcc -g -O -c hello.c
     burger$

Shared libraries, however, may only be built from position-independent code (PIC). So, special flags must be passed to the compiler to tell it to generate PIC rather than the standard position-dependent code.

Since this is a library implementation detail, libtool hides the complexity of PIC compiler flags and uses separate library object files (the PIC one lives in the .libs subdirectory and the static one lives in the current directory). On systems without shared libraries, the PIC library object files are not created, whereas on systems where all code is PIC, such as AIX, the static ones are not created.

To create library object files for foo.c and hello.c, simply invoke libtool with the standard compilation command as arguments (see Compile mode):

     a23$ libtool --mode=compile gcc -g -O -c foo.c
     gcc -g -O -c foo.c -o foo.o
     a23$ libtool --mode=compile gcc -g -O -c hello.c
     gcc -g -O -c hello.c -o hello.o
     a23$

Note that libtool silently creates an additional control file on each ‘compile’ invocation. The .lo file is the libtool object, which Libtool uses to determine what object file may be built into a shared library. On ‘a23’, only static libraries are supported so the library objects look like this:

     # foo.lo - a libtool object file
     # Generated by ltmain.sh (GNU libtool) 2.4.2
     #
     # Please DO NOT delete this file!
     # It is necessary for linking the library.
     
     # Name of the PIC object.
     pic_object=none
     
     # Name of the non-PIC object.
     non_pic_object='foo.o'

On shared library systems, libtool automatically generates an additional PIC object by inserting the appropriate PIC generation flags into the compilation command:

     burger$ libtool --mode=compile gcc -g -O -c foo.c
     mkdir .libs
     gcc -g -O -c foo.c  -fPIC -DPIC -o .libs/foo.o
     gcc -g -O -c foo.c -o foo.o >/dev/null 2>&1
     burger$

Note that Libtool automatically created .libs directory upon its first execution, where PIC library object files will be stored.

Since ‘burger’ supports shared libraries, and requires PIC objects to build them, Libtool has compiled a PIC object this time, and made a note of it in the libtool object:

     # foo.lo - a libtool object file
     # Generated by ltmain.sh (GNU libtool) 2.4.2
     #
     # Please DO NOT delete this file!
     # It is necessary for linking the library.
     
     # Name of the PIC object.
     pic_object='.libs/foo.o'
     
     # Name of the non-PIC object.
     non_pic_object='foo.o'

Notice that the second run of GCC has its output discarded. This is done so that compiler warnings aren't annoyingly duplicated. If you need to see both sets of warnings (you might have conditional code inside ‘#ifdef PIC’ for example), you can turn off suppression with the -no-suppress option to libtool's compile mode:

     burger$ libtool --mode=compile gcc -no-suppress -g -O -c hello.c
     gcc -g -O -c hello.c  -fPIC -DPIC -o .libs/hello.o
     gcc -g -O -c hello.c -o hello.o
     burger$

3.2 Linking libraries

Without libtool, the programmer would invoke the ar command to create a static library:

     burger$ ar cru libhello.a hello.o foo.o
     burger$

But of course, that would be too simple, so many systems require that you run the ranlib command on the resulting library (to give it better karma, or something):

     burger$ ranlib libhello.a
     burger$

It seems more natural to use the C compiler for this task, given libtool's “libraries are programs” approach. So, on platforms without shared libraries, libtool simply acts as a wrapper for the system ar (and possibly ranlib) commands.

Again, the libtool control file name (.la suffix) differs from the standard library name (.a suffix). The arguments to libtool are the same ones you would use to produce an executable named libhello.la with your compiler (see Link mode):

     a23$ libtool --mode=link gcc -g -O -o libhello.la foo.o hello.o
     *** Warning: Linking the shared library libhello.la against the
     *** non-libtool objects foo.o hello.o is not portable!
     ar cru .libs/libhello.a
     ranlib .libs/libhello.a
     creating libhello.la
     (cd .libs && rm -f libhello.la && ln -s ../libhello.la libhello.la)
     a23$

Aha! Libtool caught a common error... trying to build a library from standard objects instead of special .lo object files. This doesn't matter so much for static libraries, but on shared library systems, it is of great importance. (Note that you may replace libhello.la with libhello.a in which case libtool won't issue the warning any more. But although this method works, this is not intended to be used because it makes you lose the benefits of using Libtool.)

So, let's try again, this time with the library object files. Remember also that we need to add -lm to the link command line because foo.cuses the cos math library function (see Using libtool).

Another complication in building shared libraries is that we need to specify the path to the directory in which they (eventually) will be installed (in this case, /usr/local/lib)1:

     a23$ libtool --mode=link gcc -g -O -o libhello.la foo.lo hello.lo \
                     -rpath /usr/local/lib -lm
     ar cru .libs/libhello.a foo.o hello.o
     ranlib .libs/libhello.a
     creating libhello.la
     (cd .libs && rm -f libhello.la && ln -s ../libhello.la libhello.la)
     a23$

Now, let's try the same trick on the shared library platform:

     burger$ libtool --mode=link gcc -g -O -o libhello.la foo.lo hello.lo \
                     -rpath /usr/local/lib -lm
     rm -fr  .libs/libhello.a .libs/libhello.la
     ld -Bshareable -o .libs/libhello.so.0.0 .libs/foo.o .libs/hello.o -lm
     ar cru .libs/libhello.a foo.o hello.o
     ranlib .libs/libhello.a
     creating libhello.la
     (cd .libs && rm -f libhello.la && ln -s ../libhello.la libhello.la)
     burger$

Now that's significantly cooler... Libtool just ran an obscure ld command to create a shared library, as well as the static library.

Note how libtool creates extra files in the .libs subdirectory, rather than the current directory. This feature is to make it easier to clean up the build directory, and to help ensure that other programs fail horribly if you accidentally forget to use libtool when you should.

Again, you may want to have a look at the .la file in order to see what Libtool stores in it. In particular, you will see that Libtool uses this file to remember the destination directory for the library (the argument to -rpath) as well as the dependency on the math library (‘-lm’).

3.3 Linking executables

If you choose at this point to install the library (put it in a permanent location) before linking executables against it, then you don't need to use libtool to do the linking. Simply use the appropriate -L and -l flags to specify the library's location.

Some system linkers insist on encoding the full directory name of each shared library in the resulting executable. Libtool has to work around this misfeature by special magic to ensure that only permanent directory names are put into installed executables.

The importance of this bug must not be overlooked: it won't cause programs to crash in obvious ways. It creates a security hole, and possibly even worse, if you are modifying the library source code after you have installed the package, you will change the behaviour of the installed programs!

So, if you want to link programs against the library before you install it, you must use libtool to do the linking.

Here's the old way of linking against an uninstalled library:

     burger$ gcc -g -O -o hell.old main.o libhello.a -lm
     burger$

Libtool's way is almost the same2 (see Link mode):

     a23$ libtool --mode=link gcc -g -O -o hell main.o libhello.la
     gcc -g -O -o hell main.o  ./.libs/libhello.a -lm
     a23$

That looks too simple to be true. All libtool did was transform libhello.la to ./.libs/libhello.a, but remember that ‘a23’ has no shared libraries. Notice that Libtool also remembered that libhello.la depends on -lm, so even though we didn't specify -lm on the libtool command line3Libtool has added it to the gcc link line for us.

On ‘burger’ Libtool links against the uninstalled shared library:

     burger$ libtool --mode=link gcc -g -O -o hell main.o libhello.la
     gcc -g -O -o .libs/hell main.o -L./.libs -R/usr/local/lib -lhello -lm
     creating hell
     burger$

Now assume libhello.la had already been installed, and you want to link a new program with it. You could figure out where it lives by yourself, then run:

     burger$ gcc -g -O -o test test.o -L/usr/local/lib -lhello -lm

However, unless /usr/local/lib is in the standard library search path, you won't be able to run test. However, if you use libtool to link the already-installed libtool library, it will do The Right Thing (TM) for you:

     burger$ libtool --mode=link gcc -g -O -o test test.o \
                     /usr/local/lib/libhello.la
     gcc -g -O -o .libs/test test.o -Wl,--rpath \
             -Wl,/usr/local/lib /usr/local/lib/libhello.a -lm
     creating test
     burger$

Note that libtool added the necessary run-time path flag, as well as -lm, the library libhello.la depended upon. Nice, huh?

Notice that the executable, hell, was actually created in the .libs subdirectory. Then, a wrapper script (or, on certain platforms, a wrapper executable see Wrapper executables) was created in the current directory.

Since libtool created a wrapper script, you should use libtool to install it and debug it too. However, since the program does not depend on any uninstalled libtool library, it is probably usable even without the wrapper script.

On NetBSD 1.2, libtool encodes the installation directory of libhello, by using the ‘-R/usr/local/lib’ compiler flag. Then, the wrapper script guarantees that the executable finds the correct shared library (the one in ./.libs) until it is properly installed.

Let's compare the two different programs:

     burger$ time ./hell.old
     Welcome to GNU Hell!
     ** This is not GNU Hello.  There is no built-in mail reader. **
             0.21 real         0.02 user         0.08 sys
     burger$ time ./hell
     Welcome to GNU Hell!
     ** This is not GNU Hello.  There is no built-in mail reader. **
             0.63 real         0.09 user         0.59 sys
     burger$

The wrapper script takes significantly longer to execute, but at least the results are correct, even though the shared library hasn't been installed yet.

So, what about all the space savings that shared libraries are supposed to yield?

     burger$ ls -l hell.old libhello.a
     -rwxr-xr-x  1 gord  gord  15481 Nov 14 12:11 hell.old
     -rw-r--r--  1 gord  gord   4274 Nov 13 18:02 libhello.a
     burger$ ls -l .libs/hell .libs/libhello.*
     -rwxr-xr-x  1 gord  gord  11647 Nov 14 12:10 .libs/hell
     -rw-r--r--  1 gord  gord   4274 Nov 13 18:44 .libs/libhello.a
     -rwxr-xr-x  1 gord  gord  12205 Nov 13 18:44 .libs/libhello.so.0.0
     burger$

Well, that sucks. Maybe I should just scrap this project and take up basket weaving.

Actually, it just proves an important point: shared libraries incur overhead because of their (relative) complexity. In this situation, the price of being dynamic is eight kilobytes, and the payoff is about four kilobytes. So, having a shared libhello won't be an advantage until we link it against at least a few more programs.

3.3.1 Wrapper executables for uninstalled programs

Some platforms, notably those hosted on Windows such as Cygwin and MinGW, use a wrapper executable rather than a wrapper script to ensure proper operation of uninstalled programs linked by libtool against uninstalled shared libraries. The wrapper executable thus performs the same function as the wrapper script used on other platforms, but allows to satisfy the make rules for the program, whose name ends in $(EXEEXT). The actual program executable is created below .libs, and its name will end in $(EXEEXT) and may or may not contain an lt- prefix. This wrapper executable sets various environment values so that the program executable may locate its (uninstalled) shared libraries, and then launches the program executable.

The wrapper executable provides a debug mode, enabled by passing the command-line option --lt-debug (see below). When executing in debug mode, diagnostic information will be printed to stderr before the program executable is launched.

Finally, the wrapper executable supports a number of command line options that may be useful when debugging the operation of the wrapper system. All of these options begin with --lt-, and if present they and their arguments will be removed from the argument list passed on to the program executable. Therefore, the program executable may not employ command line options that begin with --lt-. (In fact, the wrapper executable will detect any command line options that begin with --lt- and abort with an error message if the option is not recognized). If this presents a problem, please contact the Libtool team at the Libtool bug reporting address bug-libtool@gnu.org.

These command line options include:

--lt-dump-script
Causes the wrapper to print a copy of the wrapper script to stdout, and exit.
--lt-debug
Causes the wrapper to print diagnostic information to stdout, before launching the program executable.

For consistency, both the wrapper script and the wrapper executable support these options.

3.4 Debugging executables

If hell was a complicated program, you would certainly want to test and debug it before installing it on your system. In the above section, you saw how the libtool wrapper script makes it possible to run the program directly, but unfortunately, this mechanism interferes with the debugger:

     burger$ gdb hell
     GDB is free software and you are welcome to distribute copies of it
      under certain conditions; type "show copying" to see the conditions.
     There is no warranty for GDB; type "show warranty" for details.
     GDB 4.16 (i386-unknown-netbsd), (C) 1996 Free Software Foundation, Inc.
     
     "hell": not in executable format: File format not recognized
     
     (gdb) quit
     burger$

Sad. It doesn't work because GDB doesn't know where the executable lives. So, let's try again, by invoking GDB directly on the executable:

     burger$ gdb .libs/hell
     GNU gdb 5.3 (i386-unknown-netbsd)
     Copyright 2002 Free Software Foundation, Inc.
     GDB is free software, covered by the GNU General Public License,
     and you are welcome to change it and/or distribute copies of it
     under certain conditions.  Type "show copying" to see the conditions.
     There is no warranty for GDB.  Type "show warranty" for details.
     (gdb) break main
     Breakpoint 1 at 0x8048547: file main.c, line 29.
     (gdb) run
     Starting program: /home/src/libtool/demo/.libs/hell
     /home/src/libtool/demo/.libs/hell: can't load library 'libhello.so.0'
     
     Program exited with code 020.
     (gdb) quit
     burger$

Argh. Now GDB complains because it cannot find the shared library that hell is linked against. So, we must use libtool in order to properly set the library path and run the debugger. Fortunately, we can forget all about the .libs directory, and just run it on the executable wrapper (see Execute mode):

     burger$ libtool --mode=execute gdb hell
     GNU gdb 5.3 (i386-unknown-netbsd)
     Copyright 2002 Free Software Foundation, Inc.
     GDB is free software, covered by the GNU General Public License,
     and you are welcome to change it and/or distribute copies of it
     under certain conditions.  Type "show copying" to see the conditions.
     There is no warranty for GDB.  Type "show warranty" for details.
     (gdb) break main
     Breakpoint 1 at 0x8048547: file main.c, line 29.
     (gdb) run
     Starting program: /home/src/libtool/demo/.libs/hell
     
     Breakpoint 1, main (argc=1, argv=0xbffffc40) at main.c:29
     29        printf ("Welcome to GNU Hell!\n");
     (gdb) quit
     The program is running.  Quit anyway (and kill it)? (y or n) y
     burger$

3.5 Installing libraries

Installing libraries on a non-libtool system is quite straightforward... just copy them into place:4

     burger$ su
     Password: ********
     burger# cp libhello.a /usr/local/lib/libhello.a
     burger#

Oops, don't forget the ranlib command:

     burger# ranlib /usr/local/lib/libhello.a
     burger#

Libtool installation is quite simple, as well. Just use the install or cp command that you normally would (see Install mode):

     a23# libtool --mode=install cp libhello.la /usr/local/lib/libhello.la
     cp libhello.la /usr/local/lib/libhello.la
     cp .libs/libhello.a /usr/local/lib/libhello.a
     ranlib /usr/local/lib/libhello.a
     a23#

Note that the libtool library libhello.la is also installed, to help libtool with uninstallation (see Uninstall mode) and linking (see Linking executables) and to help programs with dlopening (see Dlopened modules).

Here is the shared library example:

     burger# libtool --mode=install install -c libhello.la \
                     /usr/local/lib/libhello.la
     install -c .libs/libhello.so.0.0 /usr/local/lib/libhello.so.0.0
     install -c libhello.la /usr/local/lib/libhello.la
     install -c .libs/libhello.a /usr/local/lib/libhello.a
     ranlib /usr/local/lib/libhello.a
     burger#

It is safe to specify the -s (strip symbols) flag if you use a BSD-compatible install program when installing libraries. Libtool will either ignore the -s flag, or will run a program that will strip only debugging and compiler symbols from the library.

Once the libraries have been put in place, there may be some additional configuration that you need to do before using them. First, you must make sure that where the library is installed actually agrees with the -rpath flag you used to build it.

Then, running ‘libtool -n finish libdir’ can give you further hints on what to do (see Finish mode):

     burger# libtool -n finish /usr/local/lib
     PATH="$PATH:/sbin" ldconfig -m /usr/local/lib
     -----------------------------------------------------------------
     Libraries have been installed in:
        /usr/local/lib
     
     To link against installed libraries in a given directory, LIBDIR,
     you must use the `-LLIBDIR' flag during linking.
     
      You will also need to do one of the following:
        - add LIBDIR to the `LD_LIBRARY_PATH' environment variable
          during execution
        - add LIBDIR to the `LD_RUN_PATH' environment variable
          during linking
        - use the `-RLIBDIR' linker flag
     
     See any operating system documentation about shared libraries for
     more information, such as the ld and ld.so manual pages.
     -----------------------------------------------------------------
     burger#

After you have completed these steps, you can go on to begin using the installed libraries. You may also install any executables that depend on libraries you created.

3.6 Installing executables

If you used libtool to link any executables against uninstalled libtool libraries (see Linking executables), you need to use libtool to install the executables after the libraries have been installed (see Installing libraries).

So, for our Ultrix example, we would run:

     a23# libtool --mode=install -c hell /usr/local/bin/hell
     install -c hell /usr/local/bin/hell
     a23#

On shared library systems that require wrapper scripts, libtool just ignores the wrapper script and installs the correct binary:

     burger# libtool --mode=install -c hell /usr/local/bin/hell
     install -c .libs/hell /usr/local/bin/hell
     burger#

3.7 Linking static libraries

Why return to ar and ranlib silliness when you've had a taste of libtool? Well, sometimes it is desirable to create a static archive that can never be shared. The most frequent case is when you have a set of object files that you use to build several different libraries. You can create a “convenience library” out of those objects, and link against that with the other libraries, instead of listing all the object files every time.

If you just want to link this convenience library into programs, then you could just ignore libtool entirely, and use the old ar and ranlibcommands (or the corresponding GNU Automake ‘_LIBRARIES’ rules). You can even install a convenience library using GNU Libtool, though you probably don't want to and hence GNU Automake doesn't allow you to do so.

     burger$ libtool --mode=install ./install-sh -c libhello.a \
                     /local/lib/libhello.a
     ./install-sh -c libhello.a /local/lib/libhello.a
     ranlib /local/lib/libhello.a
     burger$

Using libtool for static library installation protects your library from being accidentally stripped (if the installer used the -s flag), as well as automatically running the correct ranlib command.

But libtool libraries are more than just collections of object files: they can also carry library dependency information, which old archives do not. If you want to create a libtool static convenience library, you can omit the -rpath flag and use -static to indicate that you're only interested in a static library. When you link a program with such a library, libtool will actually link all object files and dependency libraries into the program.

If you omit both -rpath and -static, libtool will create a convenience library that can be used to create other libtool libraries, even shared ones. Just like in the static case, the library behaves as an alias to a set of object files and dependency libraries, but in this case the object files are suitable for inclusion in shared libraries. But be careful not to link a single convenience library, directly or indirectly, into a single program or library, otherwise you may get errors about symbol redefinitions.

The key is remembering that a convenience library contains PIC objects, and can be linked where a list of PIC objects makes sense; i.e. into a shared library. A static convenience library contains non-PIC objects, so can be linked into an old static library, or a program.

When GNU Automake is used, you should use noinst_LTLIBRARIES instead of lib_LTLIBRARIES for convenience libraries, so that the -rpath option is not passed when they are linked.

As a rule of thumb, link a libtool convenience library into at most one libtool library, and never into a program, and link libtool static convenience libraries only into programs, and only if you need to carry library dependency information to the user of the static convenience library.

Another common situation where static linking is desirable is in creating a standalone binary. Use libtool to do the linking and add the -all-static flag.


Next: , Previous: Using libtool, Up: Top

4 Invoking libtool

The libtool program has the following synopsis:

     libtool [option]... [mode-arg]...

and accepts the following options:

--config
Display libtool configuration variables and exit.
--debug
Dump a trace of shell script execution to standard output. This produces a lot of output, so you may wish to pipe it to less (or more) or redirect to a file.
-n
--dry-run
Don't create, modify, or delete any files, just show what commands would be executed by libtool.
--features
Display basic configuration options. This provides a way for packages to determine whether shared or static libraries will be built.
--finish
Same as --mode=finish.
-h
Display short help message.
--help
Display a help message and exit. If --mode=mode is specified, then detailed help for mode is displayed.
--help-all
Display help for the general options as well as detailed help for each operation mode, and exit.
--mode=mode
Use mode as the operation mode. When using libtool from the command line, you can give just mode (or a unique abbreviation of it) as the first argument as a shorthand for the full --mode=mode. For example, the following are equivalent:
          $ libtool --mode=execute --dry-run gdb prog.exe
          $ libtool        execute --dry-run gdb prog.exe
          $ libtool        exe     --dry-run gdb prog.exe
          $ libtool        e       --dry-run gdb prog.exe

mode must be set to one of the following:

compile
Compile a source file into a libtool object.
execute
Automatically set the library path so that another program can use uninstalled libtool-generated programs or libraries.
link
Create a library or an executable.
install
Install libraries or executables.
finish
Complete the installation of libtool libraries on the system.
uninstall
Delete installed libraries or executables.
clean
Delete uninstalled libraries or executables.

--tag=tag
Use configuration variables from tag tag (see Tags).
--preserve-dup-deps
Do not remove duplicate dependencies in libraries. When building packages with static libraries, the libraries may depend circularly on each other (shared libs can too, but for those it doesn't matter), so there are situations, where -la -lb -la is required, and the second -la may not be stripped or the link will fail. In cases where these duplications are required, this option will preserve them, only stripping the libraries that libtool knows it can safely.
--quiet
--silent
Do not print out any progress or informational messages.
-v
--verbose
Print out progress and informational messages (enabled by default), as well as additional messages not ordinary seen by default.
--no-quiet
--no-silent
Print out the progress and informational messages that are seen by default. This option has no effect on whether the additional messages seen in --verbose mode are shown.
--no-verbose
Do not print out any additional informational messages beyond those ordinarily seen by default. This option has no effect on whether the ordinary progress and informational messages enabled by --no-quiet are shown.

Thus, there are now three different message levels (not counting --debug), depending on whether the normal messages and/or the additional verbose messages are displayed. Note that there is no mechanism to diplay verbose messages, without also displaying normal messages.

default
Normal messages are displayed, verbose messages are not displayed. In addition to being the default mode, it can be forcibly achieved by using both option --no-verbose and either option --no-silent or option --no-quiet.
silent
Neither normal messages nor verbose messages are displayed. This mode can be achieved using either option --silent or option--quiet.
verbose
Both normal messages and verbose messages are displayed. This mode can be achieved using either option -v or option --verbose.

--version
Print libtool version information and exit.

The current libtool implementation is done with a shell script that needs to be invoked by the shell which configure chose for configuringlibtool (see The Autoconf Manual). This shell is set in the she-bang (‘#!’) line of the libtool script. Using a different shell may cause undefined behavior.

The mode-args are a variable number of arguments, depending on the selected operation mode. In general, each mode-arg is interpreted by programs libtool invokes, rather than libtool itself.

4.1 Compile mode

For compile mode, mode-args is a compiler command to be used in creating a “standard” object file. These arguments should begin with the name of the C compiler, and contain the -c compiler flag so that only an object file is created.

Libtool determines the name of the output file by removing the directory component from the source file name, then substituting the source code suffix (e.g. ‘.c’ for C source code) with the library object suffix, ‘.lo’.

If shared libraries are being built, any necessary PIC generation flags are substituted into the compilation command.

The following components of mode-args are treated specially:

-o
Note that the -o option is now fully supported. It is emulated on the platforms that don't support it (by locking and moving the objects), so it is really easy to use libtool, just with minor modifications to your Makefiles. Typing for example
          libtool --mode=compile gcc -c foo/x.c -o foo/x.lo

will do what you expect.

Note, however, that, if the compiler does not support -c and -o, it is impossible to compile foo/x.c without overwriting an existing ./x.o. Therefore, if you do have a source file ./x.c, make sure you introduce dependencies in your Makefile to make sure ./x.o (or ./x.lo) is re-created after any sub-directory's x.lo:

          x.o x.lo: foo/x.lo bar/x.lo

This will also ensure that make won't try to use a temporarily corrupted x.o to create a program or library. It may cause needless recompilation on platforms that support -c and -o together, but it's the only way to make it safe for those that don't.

-no-suppress
If both PIC and non-PIC objects are being built, libtool will normally suppress the compiler output for the PIC object compilation to save showing very similar, if not identical duplicate output for each object. If the -no-suppress option is given in compile mode, libtool will show the compiler output for both objects.
-prefer-pic
Libtool will try to build only PIC objects.
-prefer-non-pic
Libtool will try to build only non-PIC objects.
-shared
Even if Libtool was configured with --enable-static, the object file Libtool builds will not be suitable for static linking. Libtool will signal an error if it was configured with --disable-shared, or if the host does not support shared libraries.
-static
Even if libtool was configured with --disable-static, the object file Libtool builds will be suitable for static linking.
-Wc,flag
-Xcompiler flag
Pass a flag directly to the compiler. With -Wc,, multiple flags may be separated by commas, whereas -Xcompiler passes through commas unchanged.

4.2 Link mode

Link mode links together object files (including library objects) to form another library or to create an executable program.

mode-args consist of a command using the C compiler to create an output file (with the -o flag) from several object files.

The following components of mode-args are treated specially:

-all-static
If output-file is a program, then do not link it against any shared libraries at all. If output-file is a library, then only create a static library. In general, this flag cannot be used together with ‘disable-static’ (see LT_INIT).
-avoid-version
Tries to avoid versioning (see Versioning) for libraries and modules, i.e. no version information is stored and no symbolic links are created. If the platform requires versioning, this option has no effect.
-bindir
Pass the absolute name of the directory for installing executable programs (see Directory Variables). libtool may use this value to install shared libraries there on systems that do not provide for any library hardcoding and use the directory of a program and thePATH variable as library search path. This is typically used for DLLs on Windows or other systems using the PE (Portable Executable) format. On other systems, -bindir is ignored. The default value used is libdir/../bin for libraries installed to libdir. You should not use -bindirfor modules.
-dlopen file
Same as -dlpreopen file, if native dlopening is not supported on the host platform (see Dlopened modules) or if the program is linked with -static, -static-libtool-libs, or -all-static. Otherwise, no effect. If file is self Libtool will make sure that the program can dlopen itself, either by enabling -export-dynamic or by falling back to -dlpreopen self.
-dlpreopen file
Link file into the output program, and add its symbols to the list of preloaded symbols (see Dlpreopening). If file is self, the symbols of the program itself will be added to preloaded symbol lists. If file is force Libtool will make sure that a preloaded symbol list is always defined, regardless of whether it's empty or not.
-export-dynamic
Allow symbols from output-file to be resolved with dlsym (see Dlopened modules).
-export-symbols symfile
Tells the linker to export only the symbols listed in symfile. The symbol file should end in .sym and must contain the name of one symbol per line. This option has no effect on some platforms. By default all symbols are exported.
-export-symbols-regex regex
Same as -export-symbols, except that only symbols matching the regular expression regex are exported. By default all symbols are exported.
-Llibdir
Search libdir for required libraries that have already been installed.
-lname
output-file requires the installed library libname. This option is required even when output-file is not an executable.
-module
Creates a library that can be dlopened (see Dlopened modules). This option doesn't work for programs. Module names don't need to be prefixed with ‘lib’. In order to prevent name clashes, however, libname and name must not be used at the same time in your package.
-no-fast-install
Disable fast-install mode for the executable output-file. Useful if the program won't be necessarily installed.
-no-install
Link an executable output-file that can't be installed and therefore doesn't need a wrapper script on systems that allow hardcoding of library paths. Useful if the program is only used in the build tree, e.g., for testing or generating other files.
-no-undefined
Declare that output-file does not depend on any libraries other than the ones listed on the command line, i.e., after linking, it will not have unresolved symbols. Some platforms require all symbols in shared libraries to be resolved at library creation (see Inter-library dependencies), and using this parameter allows libtool to assume that this will not happen.
-o output-file
Create output-file from the specified objects and libraries.
-objectlist file
Use a list of object files found in file to specify objects.
-precious-files-regex regex
Prevents removal of files from the temporary output directory whose names match this regular expression. You might specify ‘\.bbg?$’ to keep those files created with gcc -ftest-coverage for example.
-release release
Specify that the library was generated by release release of your package, so that users can easily tell which versions are newer than others. Be warned that no two releases of your package will be binary compatible if you use this flag. If you want binary compatibility, use the -version-info flag instead (see Versioning).
-rpath libdir
If output-file is a library, it will eventually be installed in libdir. If output-file is a program, add libdir to the run-time path of the program. On platforms that don't support hardcoding library paths into executables and only search PATH for shared libraries, such as when output-file is a Windows (or other PE platform) DLL, the .la control file will be installed in libdir, but see -bindir above for the eventual destination of the .dll or other library file itself.
-R libdir
If output-file is a program, add libdir to its run-time path. If output-file is a library, add -Rlibdir to its dependency_libs, so that, whenever the library is linked into a program, libdir will be added to its run-time path.
-shared
If output-file is a program, then link it against any uninstalled shared libtool libraries (this is the default behavior). If output-file is a library, then only create a shared library. In the later case, libtool will signal an error if it was configured with --disable-shared, or if the host does not support shared libraries.
-shrext suffix
If output-file is a libtool library, replace the system's standard file name extension for shared libraries with suffix (most systems use.so here). This option is helpful in certain cases where an application requires that shared libraries (typically modules) have an extension other than the default one. Please note you must supply the full file name extension including any leading dot.
-static
If output-file is a program, then do not link it against any uninstalled shared libtool libraries. If output-file is a library, then only create a static library.
-static-libtool-libs
If output-file is a program, then do not link it against any shared libtool libraries. If output-file is a library, then only create a static library.
-version-info current[:revision[:age]]
If output-file is a libtool library, use interface version information current, revision, and age to build it (see Versioning). Do not use this flag to specify package release information, rather see the -release flag.
-version-number major[:minor[:revision]]
If output-file is a libtool library, compute interface version information so that the resulting library uses the specified major, minor and revision numbers. This is designed to permit libtool to be used with existing projects where identical version numbers are already used across operating systems. New projects should use the -version-info flag instead.
-weak libname
if output-file is a libtool library, declare that it provides a weak libname interface. This is a hint to libtool that there is no need to append libname to the list of dependency libraries of output-file, because linking against output-file already supplies the same interface (see Linking with dlopened modules).
-Wc,flag
-Xcompiler flag
Pass a linker-specific flag directly to the compiler. With -Wc,, multiple flags may be separated by commas, whereas -Xcompiler passes through commas unchanged.
-Wl,flag
-Xlinker flag
Pass a linker-specific flag directly to the linker.
-XCClinker flag
Pass a link-specific flag to the compiler driver (CC) during linking.

If the output-file ends in .la, then a libtool library is created, which must be built only from library objects (.lo files). The -rpath option is required. In the current implementation, libtool libraries may not depend on other uninstalled libtool libraries (see Inter-library dependencies).

If the output-file ends in .a, then a standard library is created using ar and possibly ranlib.

If output-file ends in .o or .lo, then a reloadable object file is created from the input files (generally using ‘ld -r’). This method is often calledpartial linking.

Otherwise, an executable program is created.

4.3 Execute mode

For execute mode, the library path is automatically set, then a program is executed.

The first of the mode-args is treated as a program name, with the rest as arguments to that program.

The following components of mode-args are treated specially:

-dlopen file
Add the directory containing file to the library path.

This mode sets the library path environment variable according to any -dlopen flags.

If any of the args are libtool executable wrappers, then they are translated into the name of their corresponding uninstalled binary, and any of their required library directories are added to the library path.

4.4 Install mode

In install mode, libtool interprets most of the elements of mode-args as an installation command beginning with cp, or a BSD-compatibleinstall program.

The following components of mode-args are treated specially:

-inst-prefix-dir inst-prefix-dir
When installing into a temporary staging area, rather than the final prefix, this argument is used to reflect the temporary path, in much the same way automake uses DESTDIR. For instance, if prefix is /usr/local, but inst-prefix-dir is /tmp, then the object will be installed under /tmp/usr/local/. If the installed object is a libtool library, then the internal fields of that library will reflect only prefix, not inst-prefix-dir:
          # Directory that this library needs to be installed in:
          libdir='/usr/local/lib'

not

          # Directory that this library needs to be installed in:
          libdir='/tmp/usr/local/lib'

inst-prefix is also used to insure that if the installed object must be relinked upon installation, that it is relinked against the libraries ininst-prefix-dir/prefix, not prefix.

In truth, this option is not really intended for use when calling libtool directly; it is automatically used when libtool --mode=install callslibtool --mode=relink. Libtool does this by analyzing the destination path given in the original libtool --mode=install command and comparing it to the expected installation path established during libtool --mode=link.

Thus, end-users need change nothing, and automake-style make install DESTDIR=/tmp will Just Work(tm) most of the time. For systems where fast installation can not be turned on, relinking may be needed. In this case, a ‘DESTDIR’ install will fail.

Currently it is not generally possible to install into a temporary staging area that contains needed third-party libraries which are not yet visible at their final location.

The rest of the mode-args are interpreted as arguments to the cp or install command.

The command is run, and any necessary unprivileged post-installation commands are also completed.

4.5 Finish mode

Finish mode has two functions. One is to help system administrators install libtool libraries so that they can be located and linked into user programs. To invoke this functionality, pass the name of a library directory as mode-arg. Running this command may require superuser privileges, and the --dry-run option may be useful.

The second is to facilitate transferring libtool libraries to a native compilation environment after they were built in a cross-compilation environment. Cross-compilation environments may rely on recent libtool features, and running libtool in finish mode will make it easier to work with older versions of libtool. This task is performed whenever the mode-arg is a .la file.

4.6 Uninstall mode

Uninstall mode deletes installed libraries, executables and objects.

The first mode-arg is the name of the program to use to delete files (typically /bin/rm).

The remaining mode-args are either flags for the deletion program (beginning with a ‘-’), or the names of files to delete.

4.7 Clean mode

Clean mode deletes uninstalled libraries, executables, objects and libtool's temporary files associated with them.

The first mode-arg is the name of the program to use to delete files (typically /bin/rm).

The remaining mode-args are either flags for the deletion program (beginning with a ‘-’), or the names of files to delete.


Next: , Previous: Invoking libtool, Up: Top

5 Integrating libtool with your package

This chapter describes how to integrate libtool with your packages so that your users can install hassle-free shared libraries.

There are several ways in which Libtool may be integrated in your package, described in the following sections. Typically, the Libtool macro files as well as ltmain.sh are copied into your package using libtoolize and aclocal after setting up the configure.ac and toplevel Makefile.am, then autoconf adds the needed tests to the configure script. These individual steps are often automated with autoreconf.

Here is a diagram showing how such a typical Libtool configuration works when preparing a package for distribution, assuming that m4has been chosen as location for additional Autoconf macros, and build-aux as location for auxiliary build tools (see The Autoconf Manual):

     libtool.m4 -----.                .--> aclocal.m4 -----.
     ltoptions.m4 ---+  .-> aclocal* -+                    +--> autoconf*
     ltversion.m4 ---+--+             `--> [copy in m4/] --+       |
     ltsugar.m4 -----+  |                    ^             |       \/
     lt~obsolete.m4 -+  +-> libtoolize* -----'             |    configure
     [ltdl.m4] ------+  |                                  |
                        `----------------------------------'
     
     ltmain.sh -----------> libtoolize* -> [copy in build-aux/]

During configuration, the libtool script is generated either through config.status or config.lt:

                  .--> config.status* --.
     configure* --+                     +--> libtool
                  `--> [config.lt*] ----'      ^
                                               |
     ltmain.sh --------------------------------'

At make run time, libtool is then invoked as needed as a wrapper around compilers, linkers, install and cleanup programs.

There are alternatives choices to several parts of the setup; for example, the Libtool macro files can either be copied or symlinked into the package, or copied into aclocal.m4. As another example, an external, pre-configured libtool script may be used, by-passing most of the tests and package-specific setup for Libtool.

5.1 Autoconf macros exported by libtool

Libtool uses a number of macros to interrogate the host system when it is being built, and you can use some of them yourself too. Although there are a great many other macros in the libtool installed m4 files, these do not form part of the published interface, and are subject to change between releases.

Macros in the ‘LT_CMD_’ namespace check for various shell commands:

— Macro: LT_CMD_MAX_LEN

Finds the longest command line that can be safely passed to ‘$SHELL’ without being truncated, and store in the shell variable ‘$max_cmd_len’. It is only an approximate value, but command lines of this length or shorter are guaranteed not to be truncated.

Macros in the ‘LT_FUNC_’ namespace check characteristics of library functions:

— Macro: LT_FUNC_DLSYM_USCORE

AC_DEFINE’ the preprocessor symbol ‘DLSYM_USCORE’ if we have to add an underscore to symbol-names passed in to ‘dlsym’.

Macros in the ‘LT_LIB_’ namespace check characteristics of system libraries:

— Macro: LT_LIB_M

Set ‘LIBM’ to the math library or libraries required on this machine, if any.

— Macro: LT_LIB_DLLOAD

This is the macro used by ‘libltdl’ to determine which dlloaders to use on this machine, if any. Several shell variables are set (and ‘AC_SUBST’ed) depending on the dlload interfaces are available on this machine. ‘LT_DLLOADERS’ contains a list of libtool libraries that can be used, and if necessary also sets ‘LIBADD_DLOPEN’ if additional system libraries are required by the ‘dlopen’ loader, and ‘LIBADD_SHL_LOAD’ if additional system libraries are required by the ‘shl_load’ loader, respectively. Finally some symbols are set in config.h depending on the loaders that are found to work: ‘HAVE_LIBDL’, ‘HAVE_SHL_LOAD’, ‘HAVE_DYLD’, ‘HAVE_DLD’.

Macros in the ‘LT_PATH_’ namespace search the system for the full path to particular system commands:

— Macro: LT_PATH_LD

Add a --with-gnu-ld option to configure. Try to find the path to the linker used by ‘$CC’, and whether it is the GNU linker. The result is stored in the shell variable ‘$LD’, which is AC_SUBSTed.

— Macro: LT_PATH_NM

Try to find a BSD-compatible nm or a MS-compatible dumpbin command on this machine. The result is stored in the shell variable ‘$NM’, which is AC_SUBSTed.

Macros in the ‘LT_SYS_’ namespace probe for system characteristics:

— Macro: LT_SYS_DLOPEN_SELF

Tests whether a program can dlopen itself, and then also whether the same program can still dlopen itself when statically linked. Results are stored in the shell variables ‘$enable_dlopen_self’ and ‘enable_dlopen_self_static’ respectively.

— Macro: LT_SYS_DLOPEN_DEPLIBS

Define the preprocessor symbol ‘LTDL_DLOPEN_DEPLIBS’ if the OS needs help to load dependent libraries for ‘dlopen’ (or equivalent).

— Macro: LT_SYS_DLSEARCH_PATH

Define the preprocessor symbol ‘LT_DLSEARCH_PATH’ to the system default library search path.

— Macro: LT_SYS_MODULE_EXT

Define the preprocessor symbol ‘LT_MODULE_EXT’ to the extension used for runtime loadable modules. If you use libltdl to open modules, then you can simply use the libtool library extension, .la.

— Macro: LT_SYS_MODULE_PATH

Define the preprocessor symbol ‘LT_MODULE_PATH_VAR’ to the name of the shell environment variable that determines the run-time module search path.

— Macro: LT_SYS_SYMBOL_USCORE

Set the shell variable ‘sys_symbol_underscore’ to ‘no’ unless the compiler prefixes global symbols with an underscore.

5.2 Writing Makefile rules for libtool

Libtool is fully integrated with Automake (see Introduction), starting with Automake version 1.2.

If you want to use libtool in a regular Makefile (or Makefile.in), you are on your own. If you're not using Automake, and you don't know how to incorporate libtool into your package you need to do one of the following:

  1. Download the latest Automake distribution from your nearest GNU mirror, install it, and start using it.
  2. Learn how to write Makefile rules by hand. They're sometimes complex, but if you're clever enough to write rules for compiling your old libraries, then you should be able to figure out new rules for libtool libraries (hint: examine the Makefile.in in the tests/demosubdirectory of the libtool distribution... note especially that it was automatically generated from the Makefile.am by Automake).

5.3 Using Automake with libtool

Libtool library support is implemented under the ‘LTLIBRARIES’ primary.

Here are some samples from the Automake Makefile.am in the libtool distribution's demo subdirectory.

First, to link a program against a libtool library, just use the ‘program_LDADD5 variable:

     bin_PROGRAMS = hell hell_static
     
     # Build hell from main.c and libhello.la
     hell_SOURCES = main.c
     hell_LDADD = libhello.la
     
     # Create a statically linked version of hell.
     hell_static_SOURCES = main.c
     hell_static_LDADD = libhello.la
     hell_static_LDFLAGS = -static

You may use the ‘program_LDFLAGS’ variable to stuff in any flags you want to pass to libtool while linking program (such as -static to avoid linking uninstalled shared libtool libraries).

Building a libtool library is almost as trivial... note the use of ‘libhello_la_LDFLAGS’ to pass the -version-info (see Versioning) option to libtool:

     # Build a libtool library, libhello.la for installation in libdir.
     lib_LTLIBRARIES = libhello.la
     libhello_la_SOURCES = hello.c foo.c
     libhello_la_LDFLAGS = -version-info 3:12:1

The -rpath option is passed automatically by Automake (except for libraries listed as noinst_LTLIBRARIES), so you should not specify it.

See Building a Shared Library, for more information.

5.4 Configuring libtool

Libtool requires intimate knowledge of your compiler suite and operating system in order to be able to create shared libraries and link against them properly. When you install the libtool distribution, a system-specific libtool script is installed into your binary directory.

However, when you distribute libtool with your own packages (see Distributing), you do not always know the compiler suite and operating system that are used to compile your package.

For this reason, libtool must be configured before it can be used. This idea should be familiar to anybody who has used a GNU configurescript. configure runs a number of tests for system features, then generates the Makefiles (and possibly a config.h header file), after which you can run make and build the package.

Libtool adds its own tests to your configure script in order to generate a libtool script for the installer's host machine.

5.4.1 The LT_INIT macro

If you are using GNU Autoconf (or Automake), you should add a call to LT_INIT to your configure.ac file. This macro adds many new tests to the configure script so that the generated libtool script will understand the characteristics of the host. It's the most important of a number of macros defined by Libtool:

— Macro: LT_PREREQ (version)

Ensure that a recent enough version of Libtool is being used. If the version of Libtool used for LT_INIT is earlier than version, print an error message to the standard error output and exit with failure (exit status is 63). For example:

          LT_PREREQ([2.4.2])
— Macro: LT_INIT (options)
— Macro: AC_PROG_LIBTOOL
— Macro: AM_PROG_LIBTOOL

Add support for the --enable-shared, --disable-shared, --enable-static, --disable-static, --with-pic, and --without-pic configure flags.6 AC_PROG_LIBTOOLand AM_PROG_LIBTOOL are deprecated names for older versions of this macro; autoupdate will upgrade your configure.ac files.

By default, this macro turns on shared libraries if they are available, and also enables static libraries if they don't conflict with the shared libraries. You can modify these defaults by passing either disable-shared or disable-static in the option list to LT_INIT, or using AC_DISABLE_SHARED or AC_DISABLE_STATIC.

          # Turn off shared libraries during beta-testing, since they
          # make the build process take too long.
          LT_INIT([disable-shared])

The user may specify modified forms of the configure flags --enable-shared and --enable-static to choose whether shared or static libraries are built based on the name of the package. For example, to have shared ‘bfd’ and ‘gdb’ libraries built, but not shared ‘libg++’, you can run all three configure scripts as follows:

          trick$ ./configure --enable-shared=bfd,gdb

In general, specifying --enable-shared=pkgs is the same as configuring with --enable-shared every package named in the comma-separated pkgs list, and every other package with --disable-shared. The --enable-static=pkgs flag behaves similarly, but it uses --enable-static and --disable-static. The same applies to the --enable-fast-install=pkgs flag, which uses --enable-fast-install and --disable-fast-install.

The package name ‘default’ matches any packages that have not set their name in the PACKAGE environment variable.

The --with-pic and --without-pic configure flags can be used to specify whether or not libtool uses PIC objects. By default, libtool uses PIC objects for shared libraries and non-PIC objects for static libraries. The --with-pic option also accepts a comma-separated list of package names. Specifying --with-pic=pkgs is the same as configuring every package in pkgs with --with-pic and every other package with the default configuration. The package name ‘default’ is treated the same as for --enable-shared and --enable-static.

This macro also sets the shell variable LIBTOOL_DEPS, that you can use to automatically update the libtool script if it becomes out-of-date. In order to do that, add to your configure.ac:

          LT_INIT
          AC_SUBST([LIBTOOL_DEPS])

and, to Makefile.in or Makefile.am:

          LIBTOOL_DEPS = @LIBTOOL_DEPS@
          libtool: $(LIBTOOL_DEPS)
                  $(SHELL) ./config.status libtool

If you are using GNU Automake, you can omit the assignment, as Automake will take care of it. You'll obviously have to create some dependency on libtool.

Aside from disable-static and disable-shared, there are other options that you can pass to LT_INIT to modify its behaviour. Here is a full list:

dlopen
Enable checking for dlopen support. This option should be used if the package makes use of the -dlopen and -dlpreopenlibtool flags, otherwise libtool will assume that the system does not support dlopening.
win32-dll
This option should be used if the package has been ported to build clean dlls on win32 platforms. Usually this means that any library data items are exported with __declspec(dllexport) and imported with __declspec(dllimport). If this macro is not used, libtool will assume that the package libraries are not dll clean and will build only static libraries on win32 hosts.

Provision must be made to pass -no-undefined to libtool in link mode from the package Makefile. Naturally, if you pass -no-undefined, you must ensure that all the library symbols really are defined at link time!

disable-fast-install
Change the default behaviour for LT_INIT to disable optimization for fast installation. The user may still override this default, depending on platform support, by specifying --enable-fast-install to configure.
shared
Change the default behaviour for LT_INIT to enable shared libraries. This is the default on all systems where Libtool knows how to create shared libraries. The user may still override this default by specifying --disable-shared to configure.
disable-shared
Change the default behaviour for LT_INIT to disable shared libraries. The user may still override this default by specifying --enable-shared to configure.
static
Change the default behaviour for LT_INIT to enable static libraries. This is the default on all systems where shared libraries have been disabled for some reason, and on most systems where shared libraries have been enabled. If shared libraries are enabled, the user may still override this default by specifying --disable-static to configure.
disable-static
Change the default behaviour for LT_INIT to disable static libraries. The user may still override this default by specifying --enable-static to configure.
pic-only
Change the default behaviour for libtool to try to use only PIC objects. The user may still override this default by specifying--without-pic to configure.
no-pic
Change the default behaviour of libtool to try to use only non-PIC objects. The user may still override this default by specifying --with-pic to configure.
— Macro: LT_LANG (language)

Enable libtool support for the language given if it has not yet already been enabled. Languages accepted are “C++”, “Fortran 77”, “Java”, “Go”, and “Windows Resource”.

If Autoconf language support macros such as AC_PROG_CXX are used in your configure.ac, Libtool language support will automatically be enabled.

Conversely using LT_LANG to enable language support for Libtool will automatically enable Autoconf language support as well.

Both of the following examples are therefore valid ways of adding C++ language support to Libtool.

          LT_INIT
          LT_LANG([C++])
          LT_INIT
          AC_PROG_CXX
— Macro: AC_LIBTOOL_DLOPEN

This macro is deprecated, the ‘dlopen’ option to LT_INIT should be used instead.

— Macro: AC_LIBTOOL_WIN32_DLL

This macro is deprecated, the ‘win32-dll’ option to LT_INIT should be used instead.

— Macro: AC_DISABLE_FAST_INSTALL

This macro is deprecated, the ‘disable-fast-install’ option to LT_INIT should be used instead.

— Macro: AC_DISABLE_SHARED
— Macro: AM_DISABLE_SHARED

Change the default behaviour for LT_INIT to disable shared libraries. The user may still override this default by specifying ‘--enable-shared’. The option ‘disable-shared’ to LT_INIT is a shorthand for this. AM_DISABLE_SHARED is a deprecated alias for AC_DISABLE_SHARED.

— Macro: AC_ENABLE_SHARED
— Macro: AM_ENABLE_SHARED

Change the default behaviour for LT_INIT to enable shared libraries. This is the default on all systems where Libtool knows how to create shared libraries. The user may still override this default by specifying ‘--disable-shared’. The option ‘shared’ to LT_INIT is a shorthand for this. AM_ENABLE_SHARED is a deprecated alias for AC_ENABLE_SHARED.

— Macro: AC_DISABLE_STATIC
— Macro: AM_DISABLE_STATIC

Change the default behaviour for LT_INIT to disable static libraries. The user may still override this default by specifying ‘--enable-static’. The option ‘disable-static’ to LT_INIT is a shorthand for this. AM_DISABLE_STATIC is a deprecated alias for AC_DISABLE_STATIC.

— Macro: AC_ENABLE_STATIC
— Macro: AM_ENABLE_STATIC

Change the default behaviour for LT_INIT to enable static libraries. This is the default on all systems where shared libraries have been disabled for some reason, and on most systems where shared libraries have been enabled. If shared libraries are enabled, the user may still override this default by specifying ‘--disable-static’. The option ‘static’ to LT_INIT is a shorthand for this.AM_ENABLE_STATIC is a deprecated alias for AC_ENABLE_STATIC.

The tests in LT_INIT also recognize the following environment variables:

— Variable: CC

The C compiler that will be used by the generated libtool. If this is not set, LT_INIT will look for gcc or cc.

— Variable: CFLAGS

Compiler flags used to generate standard object files. If this is not set, LT_INIT will not use any such flags. It affects only the way LT_INIT runs tests, not the produced libtool.

— Variable: CPPFLAGS

C preprocessor flags. If this is not set, LT_INIT will not use any such flags. It affects only the way LT_INIT runs tests, not the produced libtool.

— Variable: LD

The system linker to use (if the generated libtool requires one). If this is not set, LT_INIT will try to find out what is the linker used by CC.

— Variable: LDFLAGS

The flags to be used by libtool when it links a program. If this is not set, LT_INIT will not use any such flags. It affects only the way LT_INIT runs tests, not the produced libtool.

— Variable: LIBS

The libraries to be used by LT_INIT when it links a program. If this is not set, LT_INIT will not use any such flags. It affects only the way LT_INIT runs tests, not the produced libtool.

— Variable: NM

Program to use rather than checking for nm.

— Variable: RANLIB

Program to use rather than checking for ranlib.

— Variable: LN_S

A command that creates a link of a program, a soft-link if possible, a hard-link otherwise. LT_INIT will check for a suitable program if this variable is not set.

— Variable: DLLTOOL

Program to use rather than checking for dlltool. Only meaningful for Cygwin/MS-Windows.

— Variable: OBJDUMP

Program to use rather than checking for objdump. Only meaningful for Cygwin/MS-Windows.

— Variable: AS

Program to use rather than checking for as. Only used on Cygwin/MS-Windows at the moment.

— Variable: MANIFEST_TOOL

Program to use rather than checking for mt, the Manifest Tool. Only used on Cygwin/MS-Windows at the moment.

With 1.3 era libtool, if you wanted to know any details of what libtool had discovered about your architecture and environment, you had to run the script with --config and grep through the results. This idiom was supported up to and including 1.5.x era libtool, where it was possible to call the generated libtool script from configure.ac as soon as LT_INIT had completed. However, one of the features of libtool 1.4 was that the libtool configuration was migrated out of a separate ltconfig file, and added to the LT_INIT macro (nee AC_PROG_LIBTOOL), so the results of the configuration tests were available directly to code in configure.ac, rendering the call out to the generated libtool script obsolete.

Starting with libtool 2.0, the multipass generation of the libtool script has been consolidated into a single config.status pass, which happens after all the code in configure.ac has completed. The implication of this is that the libtool script does not exist during execution of code from configure.ac, and so obviously it cannot be called for --config details anymore. If you are upgrading projects that used this idiom to libtool 2.0 or newer, you should replace those calls with direct references to the equivalent Autoconf shell variables that are set by the configure time tests before being passed to config.status for inclusion in the generated libtool script.

— Macro: LT_OUTPUT

By default, the configured libtool script is generated by the call to AC_OUTPUT command, and there is rarely any need to use libtoolfrom configure. However, sometimes it is necessary to run configure time compile and link tests using libtool. You can addLT_OUTPUT to your configure.ac any time after LT_INIT and any LT_LANG calls; that done, libtool will be created by a specially generated config.lt file, and available for use in later tests.

Also, when LT_OUTPUT is used, for backwards compatibility with Automake regeneration rules, config.status will call config.lt to regenerate libtool, rather than generating the file itself.

When you invoke the libtoolize program (see Invoking libtoolize), it will tell you where to find a definition of LT_INIT. If you use Automake, theaclocal program will automatically add LT_INIT support to your configure script when it sees the invocation of LT_INIT in configure.ac.

Because of these changes, and the runtime version compatibility checks Libtool now executes, we now advise against including a copy oflibtool.m4 (and brethren) in acinclude.m4. Instead, you should set your project macro directory with AC_CONFIG_MACRO_DIR. When you libtoolizeyour project, a copy of the relevant macro definitions will be placed in your AC_CONFIG_MACRO_DIR, where aclocal can reference them directly from aclocal.m4.


Previous: LT_INIT, Up: Configuring

5.4.2 Platform-specific configuration notes

While Libtool tries to hide as many platform-specific features as possible, some have to be taken into account when configuring either the Libtool package or a libtoolized package.

  • You currently need GNU make to build the Libtool package itself.
  • On AIX there are two different styles of shared linking, one in which symbols are bound at link-time and one in which symbols are bound at runtime only, similar to ELF. In case of doubt use LDFLAGS=-Wl,-brtl for the latter style.
  • On AIX, native tools are to be preferred over binutils; especially for C++ code, if using the AIX Toolbox GCC 4.0 and binutils, configure with AR=/usr/bin/ar LD=/usr/bin/ld NM='/usr/bin/nm -B'.
  • On AIX, the /bin/sh is very slow due to its inefficient handling of here-documents. A modern shell is preferable:
              CONFIG_SHELL=/bin/bash; export $CONFIG_SHELL
              $CONFIG_SHELL ./configure [...]
    
  • For C++ code with templates, it may be necessary to specify the way the compiler will generate the instantiations. For Portland pgCC version5, use CXX='pgCC --one_instantiation_per_object' and avoid parallel make.
  • On Darwin, for C++ code with templates you need two level shared libraries. Libtool builds these by default ifMACOSX_DEPLOYMENT_TARGET is set to 10.3 or later at configure time. See rdar://problem/4135857 for more information on this issue.
  • The default shell on UNICOS 9, a ksh 88e variant, is too buggy to correctly execute the libtool script. Users are advised to install a modern shell such as GNU bash.
  • Some HP-UX sed programs are horribly broken, and cannot handle libtool's requirements, so users may report unusual problems. There is no workaround except to install a working sed (such as GNU sed) on these systems.
  • The vendor-distributed NCR MP-RAS cc programs emits copyright on standard error that confuse tests on size of conftest.err. The workaround is to specify CC when run configure with CC='cc -Hnocopyr'.
  • Any earlier DG/UX system with ELF executables, such as R3.10 or R4.10, is also likely to work, but hasn't been explicitly tested.
  • On Reliant Unix libtool has only been tested with the Siemens C-compiler and an old version of gcc provided by Marco Walther.
  • libtool.m4, ltdl.m4 and the configure.ac files are marked to use autoconf-mode, which is distributed with GNU Emacs 21, Autoconf itself, and all recent releases of XEmacs.
  • When building on some GNU/Linux systems for multilib targets libtool sometimes guesses the wrong paths that the linker and dynamic linker search by default. If this occurs, you may override libtool's guesses at configure time by setting the autoconf cache variables lt_cv_sys_lib_search_path_spec and lt_cv_sys_lib_dlsearch_path_spec respectively to the correct search paths.

5.5 Including libtool in your package

In order to use libtool, you need to include the following files with your package:

config.guess
Attempt to guess a canonical system name.
config.sub
Canonical system name validation subroutine script.
install-sh
BSD-compatible install replacement script.
ltmain.sh
A generic script implementing basic libtool functionality.

Note that the libtool script itself should not be included with your package. See Configuring.

You should use the libtoolize program, rather than manually copying these files into your package.

5.5.1 Invoking libtoolize

The libtoolize program provides a standard way to add libtool support to your package. In the future, it may implement better usage checking, or other features to make libtool even easier to use.

The libtoolize program has the following synopsis:

     libtoolize [option]...

and accepts the following options:

--copy
-c
Copy files from the libtool data directory rather than creating symlinks.
--debug
Dump a trace of shell script execution to standard output. This produces a lot of output, so you may wish to pipe it to less (or more) or redirect to a file.
--dry-run
-n
Don't run any commands that modify the file system, just print them out.
--force
-f
Replace existing libtool files. By default, libtoolize won't overwrite existing files.
--help
Display a help message and exit.
--ltdl [target-directory-name]
Install libltdl in the target-directory-name subdirectory of your package. Normally, the directory is extracted from the argument toLT_CONFIG_LTDL_DIR in configure.ac, though you can also specify a subdirectory name here if you are not using Autoconf for example. Iflibtoolize can't determine the target directory, ‘libltdl’ is used as the default.
--no-warn
Normally, Libtoolize tries to diagnose use of deprecated libtool macros and other stylistic issues. If you are deliberately using outdated calling conventions, this option prevents Libtoolize from explaining how to update your project's Libtool conventions.
--nonrecursive
If passed in conjunction with --ltdl, this option will cause the libltdl installed by ‘libtoolize’ to be set up for use with a non-recursiveautomake build. To make use of it, you will need to add the following to the Makefile.am of the parent project:
          ## libltdl/Makefile.inc appends to the following variables
          ## so we set them here before including it:
          BUILT_SOURCES   =
          
          AM_CPPFLAGS        =
          AM_LDFLAGS         =
          
          include_HEADERS    =
          noinst_LTLIBRARIES =
          lib_LTLIBRARIES   =
          EXTRA_LTLIBRARIES  =
          
          EXTRA_DIST   =
          
          CLEANFILES   =
          MOSTLYCLEANFILES   =
          
          include libltdl/Makefile.inc
--quiet
-q
Work silently. ‘libtoolize --quiet’ is used by GNU Automake to add libtool files to your package if necessary.
--recursive
If passed in conjunction with --ltdl, this option will cause the libtoolize installed ‘libltdl’ to be set up for use with a recursive automakebuild. To make use of it, you will need to adjust the parent project's configure.ac:
          AC_CONFIG_FILES([libltdl/Makefile])

and Makefile.am:

          SUBDIRS += libltdl

--subproject
If passed in conjunction with --ltdl, this option will cause the libtoolize installed ‘libltdl’ to be set up for independent configuration and compilation as a self-contained subproject. To make use of it, you should arrange for your build to call libltdl/configure, and then runmake in the libltdl directory (or the subdirectory you put libltdl into). If your project uses Autoconf, you can use the supplied ‘LT_WITH_LTDL’ macro, or else call ‘AC_CONFIG_SUBDIRS’ directly.

Previous releases of ‘libltdl’ built exclusively in this mode, but now it is the default mode both for backwards compatibility and because, for example, it is suitable for use in projects that wish to use ‘libltdl’, but not use the Autotools for their own build process.

--verbose
-v
Work noisily! Give a blow by blow account of what libtoolize is doing.
--version
Print libtoolize version information and exit.

Sometimes it can be useful to pass options to libtoolize even though it is called by another program, such as autoreconf. A limited number of options are parsed from the environment variable LIBTOOLIZE_OPTIONS: currently --debug, --no-warn, --quiet and --verbose. Multiple options passed in LIBTOOLIZE_OPTIONS must be separated with a space, comma or a colon.

By default, a warning is issued for unknown options found in LIBTOOLIZE_OPTIONS unless the first such option is --no-warn. Where libtoolize has always quit on receipt of an unknown option at the command line, this and all previous releases of libtoolize will continue unabated whatever the content of LIBTOOLIZE_OPTIONS (modulo some possible warning messages).

     trick$ LIBTOOLIZE_OPTIONS=--no-warn,--quiet autoreconf --install

If libtoolize detects an explicit call to AC_CONFIG_MACRO_DIR (see The Autoconf Manual) in your configure.ac, it will put the Libtool macros in the specified directory.

In the future other Autotools will automatically check the contents of AC_CONFIG_MACRO_DIR, but at the moment it is more portable to add the macro directory to ACLOCAL_AMFLAGS in Makefile.am, which is where the tools currently look. If libtoolize doesn't see AC_CONFIG_MACRO_DIR, it too will honour the first ‘-I’ argument in ACLOCAL_AMFLAGS when choosing a directory to store libtool configuration macros in. It is perfectly sensible to use both AC_CONFIG_MACRO_DIR and ACLOCAL_AMFLAGS, as long as they are kept in synchronisation.

     ACLOCAL_AMFLAGS = -I m4

When you bootstrap your project with aclocal, then you will need to explicitly pass the same macro directory with aclocal's ‘-I’ flag:

     trick$ aclocal -I m4

If libtoolize detects an explicit call to AC_CONFIG_AUX_DIR (see The Autoconf Manual) in your configure.ac, it will put the other support files in the specified directory. Otherwise they too end up in the project root directory.

Unless --no-warn is passed, libtoolize displays hints for adding libtool support to your package, as well.

5.5.2 Autoconf and LTLIBOBJS

People used to add code like the following to their configure.ac:

     LTLIBOBJS=`echo "$LIBOBJS" | sed 's/\.[^.]* /.lo /g;s/\.[^.]*$/.lo/'`
     AC_SUBST([LTLIBOBJS])

This is no longer required (since Autoconf 2.54), and doesn't take Automake's deansification support into account either, so doesn't work correctly even with ancient Autoconfs!

Provided you are using a recent (2.54 or better) incarnation of Autoconf, the call to AC_OUTPUT takes care of setting LTLIBOBJS up correctly, so you can simply delete such snippets from your configure.ac if you had them.

5.6 Static-only libraries

When you are developing a package, it is often worthwhile to configure your package with the --disable-shared flag, or to override the defaults for LT_INIT by using the disable-shared option (see The LT_INIT macro). This prevents libtool from building shared libraries, which has several advantages:

  • compilation is twice as fast, which can speed up your development cycle,
  • debugging is easier because you don't need to deal with any complexities added by shared libraries, and
  • you can see how libtool behaves on static-only platforms.

You may want to put a small note in your package README to let other developers know that --disable-shared can save them time. The following example note is taken from the GIMP7 distribution README:

     The GIMP uses GNU Libtool in order to build shared libraries on a
     variety of systems.  While this is very nice for making usable
     binaries, it can be a pain when trying to debug a program.  For that
     reason, compilation of shared libraries can be turned off by
     specifying the --disable-shared option to configure.

Next: , Previous: Integrating libtool, Up: Top

6 Using libtool with other languages

Libtool was first implemented in order to add support for writing shared libraries in the C language. However, over time, libtool is being integrated with other languages, so that programmers are free to reap the benefits of shared libraries in their favorite programming language.

This chapter describes how libtool interacts with other languages, and what special considerations you need to make if you do not use C.

6.1 Writing libraries for C++

Creating libraries of C++ code should be a fairly straightforward process, because its object files differ from C ones in only three ways:

  1. Because of name mangling, C++ libraries are only usable by the C++ compiler that created them. This decision was made by the designers of C++ in order to protect users from conflicting implementations of features such as constructors, exception handling, and RTTI.
  2. On some systems, the C++ compiler must take special actions for the dynamic linker to run dynamic (i.e., run-time) initializers. This means that we should not call ld directly to link such libraries, and we should use the C++ compiler instead.
  3. C++ compilers will link some Standard C++ library in by default, but libtool does not know which are these libraries, so it cannot even run the inter-library dependence analyzer to check how to link it in. Therefore, running ld to link a C++ program or library is deemed to fail.

Because of these three issues, Libtool has been designed to always use the C++ compiler to compile and link C++ programs and libraries. In some instances the main() function of a program must also be compiled with the C++ compiler for static C++ objects to be properly initialized.

6.2 Tags

Libtool supports multiple languages through the use of tags. Technically a tag corresponds to a set of configuration variables associated with a language. These variables tell libtool how it should create objects and libraries for each language.

Tags are defined at configure-time for each language activated in the package (see LT_LANG in LT_INIT). Here is the correspondence between language names and tags names.

Language nameTag name
CCC
C++CXX
JavaGCJ
Fortran 77F77
FortranFC
GoGO
Windows ResourceRC

libtool tries to automatically infer which tag to use from the compiler command being used to compile or link. If it can't infer a tag, then it defaults to the configuration for the C language.

The tag can also be specified using libtool's --tag=tag option (see Invoking libtool). It is a good idea to do so in Makefile rules, because that will allow users to substitute the compiler without relying on libtool inference heuristics. When no tag is specified, libtool will default to CC; this tag always exists.

Finally, the set of tags available in a particular project can be retrieved by tracing for the LT_SUPPORTED_TAG macro (see Trace interface).


Next: , Previous: Other languages, Up: Top

7 Library interface versions

The most difficult issue introduced by shared libraries is that of creating and resolving runtime dependencies. Dependencies on programs and libraries are often described in terms of a single name, such as sed. So, one may say “libtool depends on sed,” and that is good enough for most purposes.

However, when an interface changes regularly, we need to be more specific: “Gnus 5.1 requires Emacs 19.28 or above.” Here, the description of an interface consists of a name, and a “version number.”

Even that sort of description is not accurate enough for some purposes. What if Emacs 20 changes enough to break Gnus 5.1?

The same problem exists in shared libraries: we require a formal version system to describe the sorts of dependencies that programs have on shared libraries, so that the dynamic linker can guarantee that programs are linked only against libraries that provide the interface they require.

7.1 What are library interfaces?

Interfaces for libraries may be any of the following (and more):

  • global variables: both names and types
  • global functions: argument types and number, return types, and function names
  • standard input, standard output, standard error, and file formats
  • sockets, pipes, and other inter-process communication protocol formats

Note that static functions do not count as interfaces, because they are not directly available to the user of the library.

7.2 Libtool's versioning system

Libtool has its own formal versioning system. It is not as flexible as some, but it is definitely the simplest of the more powerful versioning systems.

Think of a library as exporting several sets of interfaces, arbitrarily represented by integers. When a program is linked against a library, it may use any subset of those interfaces.

Libtool's description of the interfaces that a program uses is simple: it encodes the least and the greatest interface numbers in the resulting binary (first-interface, last-interface).

The dynamic linker is guaranteed that if a library supports every interface number between first-interface and last-interface, then the program can be relinked against that library.

Note that this can cause problems because libtool's compatibility requirements are actually stricter than is necessary.

Say libhello supports interfaces 5, 16, 17, 18, and 19, and that libtool is used to link test against libhello.

Libtool encodes the numbers 5 and 19 in test, and the dynamic linker will only link test against libraries that support every interface between 5 and 19. So, the dynamic linker refuses to link test against libhello!

In order to eliminate this problem, libtool only allows libraries to declare consecutive interface numbers. So, libhello can declare at most that it supports interfaces 16 through 19. Then, the dynamic linker will link test against libhello.

So, libtool library versions are described by three integers:

current
The most recent interface number that this library implements.
revision
The implementation number of the current interface.
age
The difference between the newest and oldest interfaces that this library implements. In other words, the library implements all the interface numbers in the range from number current - age to current.

If two libraries have identical current and age numbers, then the dynamic linker chooses the library with the greater revision number.

7.3 Updating library version information

If you want to use libtool's versioning system, then you must specify the version information to libtool using the -version-info flag during link mode (see Link mode).

This flag accepts an argument of the form ‘current[:revision[:age]]’. So, passing -version-info 3:12:1 sets current to 3, revision to 12, and age to 1.

If either revision or age are omitted, they default to 0. Also note that age must be less than or equal to the current interface number.

Here are a set of rules to help you update your library version information:

  1. Start with version information of ‘0:0:0’ for each libtool library.
  2. Update the version information only immediately before a public release of your software. More frequent updates are unnecessary, and only guarantee that the current interface number gets larger faster.
  3. If the library source code has changed at all since the last update, then increment revision (‘c:r:a’ becomes ‘c:r+1:a’).
  4. If any interfaces have been added, removed, or changed since the last update, increment current, and set revision to 0.
  5. If any interfaces have been added since the last public release, then increment age.
  6. If any interfaces have been removed or changed since the last public release, then set age to 0.

Never try to set the interface numbers so that they correspond to the release number of your package. This is an abuse that only fosters misunderstanding of the purpose of library versions. Instead, use the -release flag (see Release numbers), but be warned that every release of your package will not be binary compatible with any other release.

The following explanation may help to understand the above rules a bit better: consider that there are three possible kinds of reactions from users of your library to changes in a shared library:

  1. Programs using the previous version may use the new version as drop-in replacement, and programs using the new version can also work with the previous one. In other words, no recompiling nor relinking is needed. In this case, bump revision only, don't touchcurrent nor age.
  2. Programs using the previous version may use the new version as drop-in replacement, but programs using the new version may use APIs not present in the previous one. In other words, a program linking against the new version may fail with “unresolved symbols” if linking against the old version at runtime: set revision to 0, bump current and age.
  3. Programs may need to be changed, recompiled, relinked in order to use the new version. Bump current, set revision and age to 0.

In the above description, programs using the library in question may also be replaced by other libraries using it.

7.4 Managing release information

Often, people want to encode the name of the package release into the shared library so that it is obvious to the user which package their programs are linked against. This convention is used especially on GNU/Linux:

     trick$ ls /usr/lib/libbfd*
     /usr/lib/libbfd.a           /usr/lib/libbfd.so.2.7.0.2
     /usr/lib/libbfd.so
     trick$

On ‘trick’, /usr/lib/libbfd.so is a symbolic link to libbfd.so.2.7.0.2, which was distributed as a part of ‘binutils-2.7.0.2’.

Unfortunately, this convention conflicts directly with libtool's idea of library interface versions, because the library interface rarely changes at the same time that the release number does, and the library suffix is never the same across all platforms.

So, in order to accommodate both views, you can use the -release flag in order to set release information for libraries for which you do not want to use -version-info. For the libbfd example, the next release that uses libtool should be built with ‘-release 2.9.0’, which will produce the following files on GNU/Linux:

     trick$ ls /usr/lib/libbfd*
     /usr/lib/libbfd-2.9.0.so     /usr/lib/libbfd.a
     /usr/lib/libbfd.so
     trick$

In this case, /usr/lib/libbfd.so is a symbolic link to libbfd-2.9.0.so. This makes it obvious that the user is dealing with ‘binutils-2.9.0’, without compromising libtool's idea of interface versions.

Note that this option causes a modification of the library name, so do not use it unless you want to break binary compatibility with any past library releases. In general, you should only use -release for package-internal libraries or for ones whose interfaces change very frequently.

8 Tips for interface design

Writing a good library interface takes a lot of practice and thorough understanding of the problem that the library is intended to solve.

If you design a good interface, it won't have to change often, you won't have to keep updating documentation, and users won't have to keep relearning how to use the library.

Here is a brief list of tips for library interface design that may help you in your exploits:

Plan ahead
Try to make every interface truly minimal, so that you won't need to delete entry points very often.
Avoid interface changes
Some people love redesigning and changing entry points just for the heck of it (note: renaming a function is considered changing an entry point). Don't be one of those people. If you must redesign an interface, then try to leave compatibility functions behind so that users don't need to rewrite their existing code.
Use opaque data types
The fewer data type definitions a library user has access to, the better. If possible, design your functions to accept a generic pointer (that you can cast to an internal data type), and provide access functions rather than allowing the library user to directly manipulate the data. That way, you have the freedom to change the data structures without changing the interface.

This is essentially the same thing as using abstract data types and inheritance in an object-oriented system.

Use header files
If you are careful to document each of your library's global functions and variables in header files, and include them in your library source files, then the compiler will let you know if you make any interface changes by accident (see C header files).
Use the static keyword (or equivalent) whenever possible
The fewer global functions your library has, the more flexibility you'll have in changing them. Static functions and variables may change forms as often as you like... your users cannot access them, so they aren't interface changes.
Be careful with array dimensions
The number of elements in a global array is part of an interface, even if the header just declares extern int foo[];. This is because on i386 and some other SVR4/ELF systems, when an application references data in a shared library the size of that data (whatever its type) is included in the application executable. If you might want to change the size of an array or string then provide a pointer not the actual array.

8.1 Writing C header files

Writing portable C header files can be difficult, since they may be read by different types of compilers:

C++ compilers
C++ compilers require that functions be declared with full prototypes, since C++ is more strongly typed than C. C functions and variables also need to be declared with the extern "C" directive, so that the names aren't mangled. See C++ libraries, for other issues relevant to using C++ with libtool.
ANSI C compilers
ANSI C compilers are not as strict as C++ compilers, but functions should be prototyped to avoid unnecessary warnings when the header file is #included.
non-ANSI C compilers
Non-ANSI compilers will report errors if functions are prototyped.

These complications mean that your library interface headers must use some C preprocessor magic in order to be usable by each of the above compilers.

foo.h in the tests/demo subdirectory of the libtool distribution serves as an example for how to write a header file that can be safely installed in a system directory.

Here are the relevant portions of that file:

     /* BEGIN_C_DECLS should be used at the beginning of your declarations,
        so that C++ compilers don't mangle their names.  Use END_C_DECLS at
        the end of C declarations. */
     #undef BEGIN_C_DECLS
     #undef END_C_DECLS
     #ifdef __cplusplus
     # define BEGIN_C_DECLS extern "C" {
     # define END_C_DECLS }
     #else
     # define BEGIN_C_DECLS /* empty */
     # define END_C_DECLS /* empty */
     #endif
     
     /* PARAMS is a macro used to wrap function prototypes, so that
        compilers that don't understand ANSI C prototypes still work,
        and ANSI C compilers can issue warnings about type mismatches. */
     #undef PARAMS
     #if defined (__STDC__) || defined (_AIX) \
             || (defined (__mips) && defined (_SYSTYPE_SVR4)) \
             || defined(WIN32) || defined(__cplusplus)
     # define PARAMS(protos) protos
     #else
     # define PARAMS(protos) ()
     #endif

These macros are used in foo.h as follows:

     #ifndef FOO_H
     #define FOO_H 1
     
     /* The above macro definitions. */
     #include "..."
     
     BEGIN_C_DECLS
     
     int foo PARAMS((void));
     int hello PARAMS((void));
     
     END_C_DECLS
     
     #endif /* !FOO_H */

Note that the #ifndef FOO_H prevents the body of foo.h from being read more than once in a given compilation.

Also the only thing that must go outside the BEGIN_C_DECLS/END_C_DECLS pair are #include lines. Strictly speaking it is only C symbol names that need to be protected, but your header files will be more maintainable if you have a single pair of these macros around the majority of the header contents.

You should use these definitions of PARAMS, BEGIN_C_DECLS, and END_C_DECLS into your own headers. Then, you may use them to create header files that are valid for C++, ANSI, and non-ANSI compilers8.

Do not be naive about writing portable code. Following the tips given above will help you miss the most obvious problems, but there are definitely other subtle portability issues. You may need to cope with some of the following issues:

  • Pre-ANSI compilers do not always support the void * generic pointer type, and so need to use char * in its place.
  • The const, inline and signed keywords are not supported by some compilers, especially pre-ANSI compilers.
  • The long double type is not supported by many compilers.

Next: , Previous: Library tips, Up: Top

9 Inter-library dependencies

By definition, every shared library system provides a way for executables to depend on libraries, so that symbol resolution is deferred until runtime.

An inter-library dependency is one in which a library depends on other libraries. For example, if the libtool library libhello uses the cosfunction, then it has an inter-library dependency on libm, the math library that implements cos.

Some shared library systems provide this feature in an internally-consistent way: these systems allow chains of dependencies of potentially infinite length.

However, most shared library systems are restricted in that they only allow a single level of dependencies. In these systems, programs may depend on shared libraries, but shared libraries may not depend on other shared libraries.

In any event, libtool provides a simple mechanism for you to declare inter-library dependencies: for every library libname that your own library depends on, simply add a corresponding -lname option to the link line when you create your library. To make an example of ourlibhello that depends on libm:

     burger$ libtool --mode=link gcc -g -O -o libhello.la foo.lo hello.lo \
                     -rpath /usr/local/lib -lm
     burger$

When you link a program against libhello, you don't need to specify the same ‘-l’ options again: libtool will do that for you, in order to guarantee that all the required libraries are found. This restriction is only necessary to preserve compatibility with static library systems and simple dynamic library systems.

Some platforms, such as Windows, do not even allow you this flexibility. In order to build a shared library, it must be entirely self-contained or it must have dependencies known at link time (that is, have references only to symbols that are found in the .lo files or the specified ‘-l’ libraries), and you need to specify the -no-undefined flag. By default, libtool builds only static libraries on these kinds of platforms.

The simple-minded inter-library dependency tracking code of libtool releases prior to 1.2 was disabled because it was not clear when it was possible to link one library with another, and complex failures would occur. A more complex implementation of this concept was re-introduced before release 1.3, but it has not been ported to all platforms that libtool supports. The default, conservative behavior is to avoid linking one library with another, introducing their inter-dependencies only when a program is linked with them.

10 Dlopened modules

It can sometimes be confusing to discuss dynamic linking, because the term is used to refer to two different concepts:

  1. Compiling and linking a program against a shared library, which is resolved automatically at run time by the dynamic linker. In this process, dynamic linking is transparent to the application.
  2. The application calling functions such as dlopen that load arbitrary, user-specified modules at runtime. This type of dynamic linking is explicitly controlled by the application.

To mitigate confusion, this manual refers to the second type of dynamic linking as dlopening a module.

The main benefit to dlopening object modules is the ability to access compiled object code to extend your program, rather than using an interpreted language. In fact, dlopen calls are frequently used in language interpreters to provide an efficient way to extend the language.

Libtool provides support for dlopened modules. However, you should indicate that your package is willing to use such support, by using the LT_INIT option ‘dlopen’ in configure.ac. If this option is not given, libtool will assume no dlopening mechanism is available, and will try to simulate it.

This chapter discusses how you as a dlopen application developer might use libtool to generate dlopen-accessible modules.

10.1 Building modules to dlopen

On some operating systems, a program symbol must be specially declared in order to be dynamically resolved with the dlsym (or equivalent) function. Libtool provides the -export-dynamic and -module link flags (see Link mode), for you to make that declaration. You need to use these flags if you are linking an application program that dlopens other modules or a libtool library that will also be dlopened.

For example, if we wanted to build a shared library, hello, that would later be dlopened by an application, we would add -module to the other link flags:

     burger$ libtool --mode=link gcc -module -o hello.la foo.lo \
                     hello.lo -rpath /usr/local/lib -lm
     burger$

If symbols from your executable are needed to satisfy unresolved references in a library you want to dlopen you will have to use the flag -export-dynamic. You should use -export-dynamic while linking the executable that calls dlopen:

     burger$ libtool --mode=link gcc -export-dynamic -o helldl main.o
     burger$

10.2 Dlpreopening

Libtool provides special support for dlopening libtool object and libtool library files, so that their symbols can be resolved even on platforms without any dlopen and dlsym functions.

Consider the following alternative ways of loading code into your program, in order of increasing “laziness”:

  1. Linking against object files that become part of the program executable, whether or not they are referenced. If an object file cannot be found, then the compile time linker refuses to create the executable.
  2. Declaring a static library to the linker, so that it is searched at link time in order to satisfy any undefined references in the above object files. If the static library cannot be found, then the compile time linker refuses to create the executable.
  3. Declaring a shared library to the runtime linker, so that it is searched at runtime in order to satisfy any undefined references in the above files. If the shared library cannot be found, then the dynamic linker aborts the program before it runs.
  4. Dlopening a module, so that the application can resolve its own, dynamically-computed references. If there is an error opening the module, or the module is not found, then the application can recover without crashing.

Libtool emulates -dlopen on static platforms by linking objects into the program at compile time, and creating data structures that represent the program's symbol table. In order to use this feature, you must declare the objects you want your application to dlopen by using the -dlopen or -dlpreopen flags when you link your program (see Link mode).

— Data Type: lt_dlsymlist typedef struct { const char *name; void *address; } lt_dlsymlist

The name attribute is a null-terminated character string of the symbol name, such as "fprintf". The address attribute is a generic pointer to the appropriate object, such as &fprintf.

— Variable: const lt_dlsymlist lt_preloaded_symbols[]

An array of lt_dlsymlist structures, representing all the preloaded symbols linked into the program proper. For each module -dlpreopened by the Libtool linked program there is an element with the name of the module and an address of 0, followed by all symbols exported from this file. For the executable itself the special name ‘@PROGRAM@’ is used. The last element of all has aname and address of 0.

To facilitate inclusion of symbol lists into libraries, lt_preloaded_symbols is ‘#define’d to a suitably unique name in ltdl.h.

This variable may not be declared const on some systems due to relocation issues.

Some compilers may allow identifiers that are not valid in ANSI C, such as dollar signs. Libtool only recognizes valid ANSI C symbols (an initial ASCII letter or underscore, followed by zero or more ASCII letters, digits, and underscores), so non-ANSI symbols will not appear inlt_preloaded_symbols.

— Function: int lt_dlpreload (const lt_dlsymlist *preloaded)

Register the list of preloaded modules preloaded. If preloaded is NULL, then all previously registered symbol lists, except the list set by lt_dlpreload_default, are deleted. Return 0 on success.

— Function: int lt_dlpreload_default (const lt_dlsymlist *preloaded)

Set the default list of preloaded modules to preloaded, which won't be deleted by lt_dlpreload. Note that this function does notrequire libltdl to be initialized using lt_dlinit and can be used in the program to register the default preloaded modules. Instead of calling this function directly, most programs will use the macro LTDL_SET_PRELOADED_SYMBOLS.

Return 0 on success.

— Macro: LTDL_SET_PRELOADED_SYMBOLS

Set the default list of preloaded symbols. Should be used in your program to initialize libltdl's list of preloaded modules.

          #include <ltdl.h>
          
          int main() {
            /* ... */
            LTDL_SET_PRELOADED_SYMBOLS();
            /* ... */
          }
— Function Type: int lt_dlpreload_callback_func (lt_dlhandle handle)

Functions of this type can be passed to lt_dlpreload_open, which in turn will call back into a function thus passed for each preloaded module that it opens.

— Function: int lt_dlpreload_open (const char *originator, lt_dlpreload_callback_func *func)

Load all of the preloaded modules for originator. For every module opened in this way, call func.

To open all of the modules preloaded into libhell.la (presumably from within the libhell.a initialisation code):

          #define preloaded_symbols lt_libhell_LTX_preloaded_symbols
          
          static int hell_preload_callback (lt_dlhandle handle);
          
          int
          hell_init (void)
          {
            ...
            if (lt_dlpreload (&preloaded_symbols) == 0)
              {
                lt_dlpreload_open ("libhell", preload_callback);
              }
            ...
          }

Note that to prevent clashes between multiple preloaded modules, the preloaded symbols are accessed via a mangled symbol name: to get the symbols preloaded into ‘libhell’, you must prefix ‘preloaded_symbols’ with ‘lt_’; the originator name, ‘libhell’ in this case; and ‘_LTX_’. That is, ‘lt_libhell_LTX_preloaded_symbols’ here.

10.3 Linking with dlopened modules

When, say, an interpreter application uses dlopened modules to extend the list of methods it provides, an obvious abstraction for the maintainers of the interpreter is to have all methods (including the built in ones supplied with the interpreter) accessed through dlopen. For one thing, the dlopening functionality will be tested even during routine invocations. For another, only one subsystem has to be written for getting methods into the interpreter.

The downside of this abstraction is, of course, that environments that provide only static linkage can't even load the intrinsic interpreter methods. Not so! We can statically link those methods by dlpreopening them.

Unfortunately, since platforms such as AIX and cygwin require that all library symbols must be resolved at compile time, the interpreter maintainers will need to provide a library to both its own dlpreopened modules, and third-party modules loaded by dlopen. In itself, that is not so bad, except that the interpreter too must provide those same symbols otherwise it will be impossible to resolve all the symbols required by the modules as they are loaded. Things are even worse if the code that loads the modules for the interpreter is itself in a library – and that is usually the case for any non-trivial application. Modern platforms take care of this by automatically loading all of a module's dependency libraries as the module is loaded (libltdl can do this even on platforms that can't do it by themselves). In the end, this leads to problems with duplicated symbols and prevents modules from loading, and prevents the application from compiling when modules are preloaded.

     ,-------------.    ,------------------.    ,-----------------.
     | Interpreter |---->     Module------------>   Third-party   |
     `-------------'    |     Loader       |    |Dlopened Modules |
                        |        |         |    `-----------------'
                        |,-------v--------.|             |
                        ||  Dlpreopened   ||             |
                        ||    Modules     ||             |
                        |`----------------'|             |
                        |        |         |             |
                        |,-------v--------.|    ,--------v--------.
                        ||Module Interface||    |Module Interface |
                        ||    Library     ||    |     Library     |
                        |`----------------'|    `-----------------'
                        `------------------'

Libtool has the concept of weak library interfaces to circumvent this problem. Recall that the code that dlopens method-provider modules for the interpreter application resides in a library: All of the modules and the dlopener library itself should be linked against the common library that resolves the module symbols at compile time. To guard against duplicate symbol definitions, and for dlpreopened modules to work at all in this scenario, the dlopener library must declare that it provides a weak library interface to the common symbols in the library it shares with the modules. That way, when libtool links the Module Loader library with some Dlpreopened Modules that were in turn linked against the Module Interface Library, it knows that the Module Loader provides an already loaded Module Interface Library to resolve symbols for the Dlpreopened Modules, and doesn't ask the compiler driver to link an identical Module Interface Library dependency library too.

In conjunction with Automake, the Makefile.am for the Module Loader might look like this:

     lib_LTLIBRARIES = libinterface.la libloader.la
     
     libinterface_la_SOURCES = interface.c interface.h
     libinterface_la_LDFLAGS = -version-info 3:2:1
     
     libloader_la_SOURCES    = loader.c
     libloader_la_LDFLAGS    = -weak libinterface.la \
                               -version-info 3:2:1 \
                               -dlpreopen ../modules/intrinsics.la
     libloader_la_LIBADD     = $(libinterface_la_OBJECTS)

And the Makefile.am for the intrinsics.la module in a sibling modules directory might look like this:

     AM_CPPFLAGS             = -I$(srcdir)/../libloader
     AM_LDFLAGS              = -no-undefined -module -avoid-version \
                               -export-dynamic
     
     noinst_LTLIBRARIES      = intrinsics.la
     
     intrinsics_la_LIBADD    = ../libloader/libinterface.la
     
     ../libloader/libinterface.la:
             cd ../libloader && $(MAKE) $(AM_MAKEFLAGS) libinterface.la

For a more complex example, see the sources of libltdl in the Libtool distribution, which is built with the help of the -weak option.

10.4 Finding the correct name to dlopen

After a library has been linked with -module, it can be dlopened. Unfortunately, because of the variation in library names, your package needs to determine the correct file to dlopen.

The most straightforward and flexible implementation is to determine the name at runtime, by finding the installed .la file, and searching it for the following lines:

     # The name that we can dlopen.
     dlname='dlname'

If dlname is empty, then the library cannot be dlopened. Otherwise, it gives the dlname of the library. So, if the library was installed as/usr/local/lib/libhello.la, and the dlname was libhello.so.3, then /usr/local/lib/libhello.so.3 should be dlopened.

If your program uses this approach, then it should search the directories listed in the LD_LIBRARY_PATH9 environment variable, as well as the directory where libraries will eventually be installed. Searching this variable (or equivalent) will guarantee that your program can find its dlopened modules, even before installation, provided you have linked them using libtool.

10.5 Unresolved dlopen issues

The following problems are not solved by using libtool's dlopen support:

  • Dlopen functions are generally only available on shared library platforms. If you want your package to be portable to static platforms, you have to use either libltdl (see Using libltdl) or develop your own alternatives to dlopening dynamic code. Most reasonable solutions involve writing wrapper functions for the dlopen family, which do package-specific tricks when dlopening is unsupported or not available on a given platform.
  • There are major differences in implementations of the dlopen family of functions. Some platforms do not even use the same function names (notably HP-UX, with its shl_load family).
  • The application developer must write a custom search function in order to discover the correct module filename to supply to dlopen.

Next: , Previous: Dlopened modules, Up: Top

11 Using libltdl

Libtool provides a small library, called libltdl, that aims at hiding the various difficulties of dlopening libraries from programmers. It consists of a few headers and small C source files that can be distributed with applications that need dlopening functionality. On some platforms, whose dynamic linkers are too limited for a simple implementation of libltdl services, it requires GNU DLD, or it will only emulate dynamic linking with libtool's dlpreopening mechanism.

libltdl supports currently the following dynamic linking mechanisms:

  • dlopen (POSIX compliant systems, GNU/Linux, etc.)
  • shl_load (HP-UX)
  • LoadLibrary (Win16 and Win32)
  • load_add_on (BeOS)
  • NSAddImage or NSLinkModule (Darwin and Mac OS X)
  • GNU DLD (emulates dynamic linking for static libraries)
  • libtool's dlpreopen (see see Dlpreopening)

libltdl is licensed under the terms of the GNU Lesser General Public License, with the following exception:

As a special exception to the GNU Lesser General Public License, if you distribute this file as part of a program or library that is built using GNU Libtool, you may include it under the same distribution terms that you use for the rest of that program.

11.1 How to use libltdl in your programs

The libltdl API is similar to the POSIX dlopen interface, which is very simple but powerful.

To use libltdl in your program you have to include the header file ltdl.h:

     #include <ltdl.h>

The early releases of libltdl used some symbols that violated the POSIX namespace conventions. These symbols are now deprecated, and have been replaced by those described here. If you have code that relies on the old deprecated symbol names, defining ‘LT_NON_POSIX_NAMESPACE’ before you include ltdl.h provides conversion macros. Whichever set of symbols you use, the new API is not binary compatible with the last, so you will need to recompile your application in order to use this version of libltdl.

Note that libltdl is not well tested in a multithreaded environment, though the intention is that it should work (see Using libltdl in a multi threaded environment). It was reported that GNU/Linux's glibc 2.0's dlopen with ‘RTLD_LAZY’ (which libltdl uses by default) is not thread-safe, but this problem is supposed to be fixed in glibc 2.1. On the other hand, ‘RTLD_NOW’ was reported to introduce problems in multi-threaded applications on FreeBSD. Working around these problems is left as an exercise for the reader; contributions are certainly welcome.

The following macros are defined by including ltdl.h:

— Macro: LT_PATHSEP_CHAR

LT_PATHSEP_CHAR is the system-dependent path separator, that is, ‘;’ on Windows and ‘:’ everywhere else.

— Macro: LT_DIRSEP_CHAR

If LT_DIRSEP_CHAR is defined, it can be used as directory separator in addition to ‘/’. On Windows, this contains ‘\’.

The following types are defined in ltdl.h:

— Type: lt_dlhandle

lt_dlhandle is a module “handle”. Every lt_dlopened module has a handle associated with it.

— Type: lt_dladvise

lt_dladvise is used to control optional module loading modes. If it is not used, the default mode of the underlying system module loader is used.

— Type: lt_dlsymlist

lt_dlsymlist is a symbol list for dlpreopened modules. This structure is described in see Dlpreopening.

libltdl provides the following functions:

— Function: int lt_dlinit (void)

Initialize libltdl. This function must be called before using libltdl and may be called several times. Return 0 on success, otherwise the number of errors.

— Function: int lt_dlexit (void)

Shut down libltdl and close all modules. This function will only then shut down libltdl when it was called as many times aslt_dlinit has been successfully called. Return 0 on success, otherwise the number of errors.

— Function: lt_dlhandle lt_dlopen (const char *filename)

Open the module with the file name filename and return a handle for it. lt_dlopen is able to open libtool dynamic modules, preloaded static modules, the program itself and native dynamic modules10.

Unresolved symbols in the module are resolved using its dependency libraries and previously dlopened modules. If the executable using this module was linked with the -export-dynamic flag, then the global symbols in the executable will also be used to resolve references in the module.

If filename is NULL and the program was linked with -export-dynamic or -dlopen self, lt_dlopen will return a handle for the program itself, which can be used to access its symbols.

If libltdl cannot find the library and the file name filename does not have a directory component it will additionally look in the following search paths for the module (in the following order):

  1. user-defined search path: This search path can be changed by the program using the functions lt_dlsetsearchpath,lt_dladdsearchdir and lt_dlinsertsearchdir.
  2. libltdl's search path: This search path is the value of the environment variable LTDL_LIBRARY_PATH.
  3. system library search path: The system dependent library search path (e.g. on GNU/Linux it is LD_LIBRARY_PATH).

Each search path must be a list of absolute directories separated by LT_PATHSEP_CHAR, for example, "/usr/lib/mypkg:/lib/foo". The directory names may not contain the path separator.

If the same module is loaded several times, the same handle is returned. If lt_dlopen fails for any reason, it returns NULL.

— Function: lt_dlhandle lt_dlopenext (const char *filename)

The same as lt_dlopen, except that it tries to append different file name extensions to the file name. If the file with the file namefilename cannot be found libltdl tries to append the following extensions:

  1. the libtool archive extension .la
  2. the extension used for native dynamically loadable modules on the host platform, e.g., .so, .sl, etc.

This lookup strategy was designed to allow programs that don't have knowledge about native dynamic libraries naming conventions to be able to dlopen such libraries as well as libtool modules transparently.

— Function: lt_dlhandle lt_dlopenadvise (const char *filename, lt_dladvise advise)

The same as lt_dlopen, except that it also requires an additional argument which may contain additional hints to the underlying system module loader. The advise parameter is opaque and can only be accessed with the functions documented below.

Note that this function does not change the content of advise, so unlike the other calls in this API takes a direct lt_dladvise type, and not a pointer to the same.

— Function: int lt_dladvise_init (lt_dladvise *advise)

The advise parameter can be used to pass hints to the module loader when using lt_dlopenadvise to perform the loading. Theadvise parameter needs to be initialised by this function before it can be used. Any memory used by advise needs to be recycled with lt_dladvise_destroy when it is no longer needed.

On failure, lt_dladvise_init returns non-zero and sets an error message that can be retrieved with lt_dlerror.

— Function: int lt_dladvise_destroy (lt_dladvise *advise)

Recycle the memory used by advise. For an example, see the documentation for lt_dladvise_ext.

On failure, lt_dladvise_destroy returns non-zero and sets an error message that can be retrieved with lt_dlerror.

— Function: int lt_dladvise_ext (lt_dladvise *advise)

Set the ext hint on advise. Passing an advise parameter to lt_dlopenadvise with this hint set causes it to try to append different file name extensions like lt_dlopenext.

The following example is equivalent to calling lt_dlopenext (filename):

          lt_dlhandle
          my_dlopenext (const char *filename)
          {
            lt_dlhandle handle = 0;
            lt_dladvise advise;
          
            if (!lt_dladvise_init (&advise) && !lt_dladvise_ext (&advise))
              handle = lt_dlopenadvise (filename, advise);
          
            lt_dladvise_destroy (&advise);
          
            return handle;
          }

On failure, lt_dladvise_ext returns non-zero and sets an error message that can be retrieved with lt_dlerror.

— Function: int lt_dladvise_global (lt_dladvise *advise)

Set the symglobal hint on advise. Passing an advise parameter to lt_dlopenadvise with this hint set causes it to try to make the loaded module's symbols globally available for resolving unresolved symbols in subsequently loaded modules.

If neither the symglobal nor the symlocal hints are set, or if a module is loaded without using the lt_dlopenadvise call in any case, then the visibility of the module's symbols will be as per the default for the underlying module loader and OS. Even if a suitable hint is passed, not all loaders are able to act upon it in which case lt_dlgetinfo will reveal whether the hint was actually followed.

On failure, lt_dladvise_global returns non-zero and sets an error message that can be retrieved with lt_dlerror.

— Function: int lt_dladvise_local (lt_dladvise *advise)

Set the symlocal hint on advise. Passing an advise parameter to lt_dlopenadvise with this hint set causes it to try to keep the loaded module's symbols hidden so that they are not visible to subsequently loaded modules.

If neither the symglobal nor the symlocal hints are set, or if a module is loaded without using the lt_dlopenadvise call in any case, then the visibility of the module's symbols will be as per the default for the underlying module loader and OS. Even if a suitable hint is passed, not all loaders are able to act upon it in which case lt_dlgetinfo will reveal whether the hint was actually followed.

On failure, lt_dladvise_local returns non-zero and sets an error message that can be retrieved with lt_dlerror.

— Function: int lt_dladvise_resident (lt_dladvise *advise)

Set the resident hint on advise. Passing an advise parameter to lt_dlopenadvise with this hint set causes it to try to make the loaded module resident in memory, so that it cannot be unloaded with a later call to lt_dlclose.

On failure, lt_dladvise_resident returns non-zero and sets an error message that can be retrieved with lt_dlerror.

— Function: int lt_dladvise_preload (lt_dladvise *advise)

Set the preload hint on advise. Passing an advise parameter to lt_dlopenadvise with this hint set causes it to load only preloaded modules, so that if a suitable preloaded module is not found, lt_dlopenadvise will return NULL.

— Function: int lt_dlclose (lt_dlhandle handle)

Decrement the reference count on the module handle. If it drops to zero and no other module depends on this module, then the module is unloaded. Return 0 on success.

— Function: void * lt_dlsym (lt_dlhandle handle, const char *name)

Return the address in the module handle, where the symbol given by the null-terminated string name is loaded. If the symbol cannot be found, NULL is returned.

— Function: const char * lt_dlerror (void)

Return a human readable string describing the most recent error that occurred from any of libltdl's functions. Return NULL if no errors have occurred since initialization or since it was last called.

— Function: int lt_dladdsearchdir (const char *search_dir)

Append the search directory search_dir to the current user-defined library search path. Return 0 on success.

— Function: int lt_dlinsertsearchdir (const char *before, const char *search_dir)

Insert the search directory search_dir into the user-defined library search path, immediately before the element starting at address before. If before is ‘NULL’, then search_dir is appending as if lt_dladdsearchdir had been called. Return 0 on success.

— Function: int lt_dlsetsearchpath (const char *search_path)

Replace the current user-defined library search path with search_path, which must be a list of absolute directories separated by LT_PATHSEP_CHAR. Return 0 on success.

— Function: const char * lt_dlgetsearchpath (void)

Return the current user-defined library search path.

— Function: int lt_dlforeachfile (const char *search_path, int (*func) (const char *filename, void * data), void * data)

In some applications you may not want to load individual modules with known names, but rather find all of the modules in a set of directories and load them all during initialisation. With this function you can have libltdl scan the LT_PATHSEP_CHAR-delimited directory list in search_path for candidates, and pass them, along with data to your own callback function, func. Ifsearch_path is ‘NULL’, then search all of the standard locations that lt_dlopen would examine. This function will continue to make calls to func for each file that it discovers in search_path until one of these calls returns non-zero, or until the files are exhausted. ‘lt_dlforeachfile’ returns the value returned by the last call made to func.

For example you could define func to build an ordered argv-like vector of files using data to hold the address of the start of the vector.

— Function: int lt_dlmakeresident (lt_dlhandle handle)

Mark a module so that it cannot be ‘lt_dlclose’d. This can be useful if a module implements some core functionality in your project that would cause your code to crash if removed. Return 0 on success.

If you use ‘lt_dlopen (NULL)’ to get a handle for the running binary, that handle will always be marked as resident, and consequently cannot be successfully ‘lt_dlclose’d.

— Function: int lt_dlisresident (lt_dlhandle handle)

Check whether a particular module has been marked as resident, returning 1 if it has or 0 otherwise. If there is an error while executing this function, return -1 and set an error message for retrieval with lt_dlerror.

11.2 Creating modules that can be dlopened

Libtool modules are created like normal libtool libraries with a few exceptions:

You have to link the module with libtool's -module switch, and you should link any program that is intended to dlopen the module with -dlopen modulename.la where possible, so that libtool can dlpreopen the module on platforms that do not support dlopening. If the module depends on any other libraries, make sure you specify them either when you link the module or when you link programs that dlopen it. If you want to disable versioning (see Versioning) for a specific module you should link it with the -avoid-version switch. Note that libtool modules don't need to have a "lib" prefix. However, Automake 1.4 or higher is required to build such modules.

Usually a set of modules provide the same interface, i.e. exports the same symbols, so that a program can dlopen them without having to know more about their internals: In order to avoid symbol conflicts all exported symbols must be prefixed with "modulename_LTX_" (modulename is the name of the module). Internal symbols must be named in such a way that they won't conflict with other modules, for example, by prefixing them with "_modulename_". Although some platforms support having the same symbols defined more than once it is generally not portable and it makes it impossible to dlpreopen such modules.

libltdl will automatically cut the prefix off to get the real name of the symbol. Additionally, it supports modules that do not use a prefix so that you can also dlopen non-libtool modules.

foo1.c gives an example of a portable libtool module. Exported symbols are prefixed with "foo1_LTX_", internal symbols with "_foo1_". Aliases are defined at the beginning so that the code is more readable.

     /* aliases for the exported symbols */
     #define foo  foo1_LTX_foo
     #define bar  foo1_LTX_bar
     
     /* a global variable definition */
     int bar = 1;
     
     /* a private function */
     int _foo1_helper() {
       return bar;
     }
     
     /* an exported function */
     int foo() {
       return _foo1_helper();
     }

The Makefile.am contains the necessary rules to build the module foo1.la:

     ...
     lib_LTLIBRARIES = foo1.la
     
     foo1_la_SOURCES = foo1.c
     foo1_la_LDFLAGS = -module
     ...

11.3 Using libltdl in a multi threaded environment

Libltdl provides a wrapper around whatever dynamic run-time object loading mechanisms are provided by the host system, many of which are themselves not thread safe. Consequently libltdl cannot itself be consistently thread safe.

If you wish to use libltdl in a multithreaded environment, then you must mutex lock around libltdl calls, since they may in turn be calling non-thread-safe system calls on some target hosts.

Some old releases of libtool provided a mutex locking API that was unusable with POSIX threads, so callers were forced to lock around all libltdl API calls anyway. That mutex locking API was next to useless, and is not present in current releases.

Some future release of libtool may provide a new POSIX thread compliant mutex locking API.

11.4 Data associated with loaded modules

Some of the internal information about each loaded module that is maintained by libltdl is available to the user, in the form of this structure:

— Type: struct lt_dlinfo { char *filename; char *name; int ref_count; int is_resident; int is_symglobal; int is_symlocal;}

lt_dlinfo is used to store information about a module. The filename attribute is a null-terminated character string of the real module file name. If the module is a libtool module then name is its module name (e.g. "libfoo" for "dir/libfoo.la"), otherwise it is set to NULL. The ref_count attribute is a reference counter that describes how often the same module is currently loaded. The remaining fields can be compared to any hints that were passed to lt_dlopenadvise to determine whether the underlying loader was able to follow them.

The following function will return a pointer to libltdl's internal copy of this structure for the given handle:

— Function: const lt_dlinfo * lt_dlgetinfo (lt_dlhandle handle)

Return a pointer to a struct that contains some information about the module handle. The contents of the struct must not be modified. Return NULL on failure.

Furthermore, in order to save you from having to keep a list of the handles of all the modules you have loaded, these functions allow you to iterate over libltdl's list of loaded modules:

— Type: lt_dlinterface_id

The opaque type used to hold the module interface details for each registered libltdl client.

— Type: int lt_dlhandle_interface (lt_dlhandle handle, const char *id_string)

Functions of this type are called to check that a handle conforms to a library's expected module interface when iterating over the global handle list. You should be careful to write a callback function of this type that can correctly identify modules that belong to this client, both to prevent other clients from accidentally finding your loaded modules with the iterator functions below, and vice versa. The best way to do this is to check that module handle conforms to the interface specification of your loader using lt_dlsym.

The callback may be given every module loaded by all the libltdl module clients in the current address space, including any modules loaded by other libraries such as libltdl itself, and should return non-zero if that module does not fulfill the interface requirements of your loader.

          int
          my_interface_cb (lt_dlhandle handle, const char *id_string)
          {
            char *(*module_id) (void) = NULL;
          
            /* A valid my_module must provide all of these symbols.  */
            if (!((module_id = (char*(*)(void)) lt_dlsym ("module_version"))
                  && lt_dlsym ("my_module_entrypoint")))
                return 1;
          
            if (strcmp (id_string, module_id()) != 0)
                return 1;
          
            return 0;
          }
— Function: lt_dlinterface_id lt_dlinterface_register (const char *id_string, lt_dlhandle_interface *iface)

Use this function to register your interface validator with libltdl, and in return obtain a unique key to store and retrieve per-module data. You supply an id_string and iface so that the resulting lt_dlinterface_id can be used to filter the module handles returned by the iteration functions below. If iface is NULL, all modules will be matched.

— Function: void lt_dlinterface_free (lt_dlinterface_id iface)

Release the data associated with iface.

— Function: int lt_dlhandle_map (lt_dlinterface_id iface, int (*func) (lt_dlhandle handle, void * data), void * data)

For each module that matches iface, call the function func. When writing the func callback function, the argument handle is the handle of a loaded module, and data is the last argument passed to lt_dlhandle_map. As soon as func returns a non-zero value for one of the handles, lt_dlhandle_map will stop calling func and immediately return that non-zero value. Otherwise 0 is eventually returned when func has been successfully called for all matching modules.

— Function: lt_dlhandle lt_dlhandle_iterate (lt_dlinterface_id  iface, lt_dlhandle place)

Iterate over the module handles loaded by iface, returning the first matching handle in the list if place is NULL, and the next one on subsequent calls. If place is the last element in the list of eligible modules, this function returns NULL.

          lt_dlhandle handle = 0;
          lt_dlinterface_id iface = my_interface_id;
          
          while ((handle = lt_dlhandle_iterate (iface, handle)))
            {
              ...
            }
— Function: lt_dlhandle lt_dlhandle_fetch (lt_dlinterface_id iface, const char *module_name)

Search through the module handles loaded by iface for a module named module_name, returning its handle if found or elseNULL if no such named module has been loaded by iface.

However, you might still need to maintain your own list of loaded module handles (in parallel with the list maintained inside libltdl) if there were any other data that your application wanted to associate with each open module. Instead, you can use the following API calls to do that for you. You must first obtain a unique interface id from libltdl as described above, and subsequently always use it to retrieve the data you stored earlier. This allows different libraries to each store their own data against loaded modules, without interfering with one another.

— Function: void * lt_dlcaller_set_data (lt_dlinterface_id key, lt_dlhandle handle, void * data)

Set data as the set of data uniquely associated with key and handle for later retrieval. This function returns the data previously associated with key and handle if any. A result of 0, may indicate that a diagnostic for the last error (if any) is available fromlt_dlerror().

For example, to correctly remove some associated data:

          void *stale = lt_dlcaller_set_data (key, handle, 0);
          if (stale != NULL)
            {
              free (stale);
            }
          else
            {
              char *error_msg = lt_dlerror ();
          
              if (error_msg != NULL)
                {
                  my_error_handler (error_msg);
                  return STATUS_FAILED;
                }
            }
— Function: void * lt_dlcaller_get_data (lt_dlinterface_id key, lt_dlhandle handle)

Return the address of the data associated with key and handle, or else NULL if there is none.

Old versions of libltdl also provided a simpler, but similar, API based around lt_dlcaller_id. Unfortunately, it had no provision for detecting whether a module belonged to a particular interface as libltdl didn't support multiple loaders in the same address space at that time. Those APIs are no longer supported as there would be no way to stop clients of the old APIs from seeing (and accidentally altering) modules loaded by other libraries.

11.5 How to create and register new module loaders

Sometimes libltdl's many ways of gaining access to modules are not sufficient for the purposes of a project. You can write your own loader, and register it with libltdl so that lt_dlopen will be able to use it.

Writing a loader involves writing at least three functions that can be called by lt_dlopen, lt_dlsym and lt_dlclose. Optionally, you can provide a finalisation function to perform any cleanup operations when lt_dlexit executes, and a symbol prefix string that will be prepended to any symbols passed to lt_dlsym. These functions must match the function pointer types below, after which they can be allocated to an instance of lt_user_dlloader and registered.

Registering the loader requires that you choose a name for it, so that it can be recognised by lt_dlloader_find and removed withlt_dlloader_remove. The name you choose must be unique, and not already in use by libltdl's builtin loaders:

"dlopen"
The system dynamic library loader, if one exists.
"dld"
The GNU dld loader, if libdld was installed when libltdl was built.
"dlpreload"
The loader for lt_dlopening of preloaded static modules.

The prefix "dl" is reserved for loaders supplied with future versions of libltdl, so you should not use that for your own loader names.

The following types are defined in ltdl.h:

— Type: lt_module

lt_module is a dlloader dependent module. The dynamic module loader extensions communicate using these low level types.

— Type: lt_dlloader

lt_dlloader is a handle for module loader types.

— Type: lt_user_data

lt_user_data is used for specifying loader instance data.

— Type: struct lt_user_dlloader {const char *sym_prefix; lt_module_open *module_open; lt_module_close *module_close;lt_find_sym *find_sym; lt_dlloader_exit *dlloader_exit; }

If you want to define a new way to open dynamic modules, and have the lt_dlopen API use it, you need to instantiate one of these structures and pass it to lt_dlloader_add. You can pass whatever you like in the dlloader_data field, and it will be passed back as the value of the first parameter to each of the functions specified in the function pointer fields.

— Type: lt_module lt_module_open (const char *filename)

The type of the loader function for an lt_dlloader module loader. The value set in the dlloader_data field of the struct lt_user_dlloaderstructure will be passed into this function in the loader_data parameter. Implementation of such a function should attempt to load the named module, and return an lt_module suitable for passing in to the associated lt_module_close and lt_sym_find function pointers. If the function fails it should return NULL, and set the error message with lt_dlseterror.

— Type: int lt_module_close (lt_user_data loader_data, lt_module module)

The type of the unloader function for a user defined module loader. Implementation of such a function should attempt to release any resources tied up by the module module, and then unload it from memory. If the function fails for some reason, set the error message with lt_dlseterror and return non-zero.

— Type: void * lt_find_sym (lt_module module, const char *symbol)

The type of the symbol lookup function for a user defined module loader. Implementation of such a function should return the address of the named symbol in the module module, or else set the error message with lt_dlseterror and return NULL if lookup fails.

— Type: int lt_dlloader_exit (lt_user_data loader_data)

The type of the finalisation function for a user defined module loader. Implementation of such a function should free any resources associated with the loader, including any user specified data in the dlloader_data field of the lt_user_dlloader. If non-NULL, the function will be called by lt_dlexit, and lt_dlloader_remove.

For example:

     int
     register_myloader (void)
     {
       lt_user_dlloader dlloader;
     
       /* User modules are responsible for their own initialisation. */
       if (myloader_init () != 0)
         return MYLOADER_INIT_ERROR;
     
       dlloader.sym_prefix    = NULL;
       dlloader.module_open   = myloader_open;
       dlloader.module_close  = myloader_close;
       dlloader.find_sym      = myloader_find_sym;
       dlloader.dlloader_exit = myloader_exit;
       dlloader.dlloader_data = (lt_user_data)myloader_function;
     
       /* Add my loader as the default module loader. */
       if (lt_dlloader_add (lt_dlloader_next (NULL), &dlloader,
                            "myloader") != 0)
         return ERROR;
     
       return OK;
     }

Note that if there is any initialisation required for the loader, it must be performed manually before the loader is registered – libltdl doesn't handle user loader initialisation.

Finalisation is handled by libltdl however, and it is important to ensure the dlloader_exit callback releases any resources claimed during the initialisation phase.

libltdl provides the following functions for writing your own module loaders:

— Function: int lt_dlloader_add (lt_dlloader *place, lt_user_dlloader *dlloader, const char *loader_name)

Add a new module loader to the list of all loaders, either as the last loader (if place is NULL), else immediately before the loader passed as place. loader_name will be returned by lt_dlloader_name if it is subsequently passed a newly registered loader. These loader_names must be unique, or lt_dlloader_remove and lt_dlloader_find cannot work. Returns 0 for success.

          /* Make myloader be the last one. */
          if (lt_dlloader_add (NULL, myloader) != 0)
            perror (lt_dlerror ());
— Function: int lt_dlloader_remove (const char *loader_name)

Remove the loader identified by the unique name, loader_name. Before this can succeed, all modules opened by the named loader must have been closed. Returns 0 for success, otherwise an error message can be obtained from lt_dlerror.

          /* Remove myloader. */
          if (lt_dlloader_remove ("myloader") != 0)
            perror (lt_dlerror ());
— Function: lt_dlloader * lt_dlloader_next (lt_dlloader *place)

Iterate over the module loaders, returning the first loader if place is NULL, and the next one on subsequent calls. The handle is for use with lt_dlloader_add.

          /* Make myloader be the first one. */
          if (lt_dlloader_add (lt_dlloader_next (NULL), myloader) != 0)
            return ERROR;
— Function: lt_dlloader * lt_dlloader_find (const char *loader_name)

Return the first loader with a matching loader_name identifier, or else NULL, if the identifier is not found.

The identifiers that may be used by libltdl itself, if the host architecture supports them are dlopen11, dld and dlpreload.

          /* Add a user loader as the next module loader to be tried if
             the standard dlopen loader were to fail when lt_dlopening. */
          if (lt_dlloader_add (lt_dlloader_find ("dlopen"), myloader) != 0)
            return ERROR;
— Function: const char * lt_dlloader_name (lt_dlloader *place)

Return the identifying name of place, as obtained from lt_dlloader_next or lt_dlloader_find. If this function fails, it will return NULL and set an error for retrieval with lt_dlerror.

— Function: lt_user_data * lt_dlloader_data (lt_dlloader *place)

Return the address of the dlloader_data of place, as obtained from lt_dlloader_next or lt_dlloader_find. If this function fails, it will returnNULL and set an error for retrieval with lt_dlerror.

11.5.1 Error handling within user module loaders

— Function: int lt_dladderror (const char *diagnostic)

This function allows you to integrate your own error messages into lt_dlerror. Pass in a suitable diagnostic message for return bylt_dlerror, and an error identifier for use with lt_dlseterror is returned.

If the allocation of an identifier fails, this function returns -1.

          int myerror = lt_dladderror ("Doh!");
          if (myerror < 0)
            perror (lt_dlerror ());
— Function: int lt_dlseterror (int errorcode)

When writing your own module loaders, you should use this function to raise errors so that they are propagated through thelt_dlerror interface. All of the standard errors used by libltdl are declared in ltdl.h, or you can add more of your own withlt_dladderror. This function returns 0 on success.

          if (lt_dlseterror (LTDL_ERROR_NO_MEMORY) != 0)
            perror (lt_dlerror ());

11.6 How to distribute libltdl with your package

Even though libltdl is installed together with libtool, you may wish to include libltdl in the distribution of your package, for the convenience of users of your package that don't have libtool or libltdl installed, or if you are using features of a very new version of libltdl that you don't expect your users to have yet. In such cases, you must decide which flavor of libltdl you want to use: a convenience library or an installable libtool library.

The most simplistic way to add libltdl to your package is to copy all the libltdl source files to a subdirectory within your package and to build and link them along with the rest of your sources. To help you do this, the m4 macros for Autoconf are available in ltdl.m4. You must ensure that they are available in aclocal.m4 before you run Autoconf12. Having made the macros available, you must add a call to the ‘LTDL_INIT’ macro (after the call to ‘LT_INIT’) to your package's configure.ac to perform the configure time checks required to build the library correctly. Unfortunately, this method has problems if you then try to link the package binaries with an installed libltdl, or a library that depends on libltdl, because of the duplicate symbol definitions. For example, ultimately linking against two different versions of libltdl, or against both a local convenience library and an installed libltdl is bad. Ensuring that only one copy of the libltdl sources are linked into any program is left as an exercise for the reader.

— Macro: LT_CONFIG_LTDL_DIR (directory)

Declare directory to be the location of the libltdl source files, for libtoolize --ltdl to place them. See Invoking libtoolize, for more details. Provided that you add an appropriate LT_CONFIG_LTDL_DIR call in your configure.ac before calling libtoolize, the appropriatelibltdl files will be installed automatically.

— Macro: LTDL_INIT (options)
— Macro: LT_WITH_LTDL
— Macro: AC_WITH_LTDL

AC_WITH_LTDL and LT_WITH_LTDL are deprecated names for older versions of this macro; autoupdate will update your configure.ac file.

This macro adds the following options to the configure script:

--with-ltdl-include installed-ltdl-header-dir
The LTDL_INIT macro will look in the standard header file locations to find the installed libltdl headers. If LTDL_INIT can't find them by itself, the person who builds your package can use this option to tell configure where the installed libltdl headers are.
--with-ltdl-lib installed-ltdl-library-dir
Similarly, the person building your package can use this option to help configure find the installed libltdl.la.
--with-included-ltdl
If there is no installed libltdl, or in any case if the person building your package would rather use the libltdl sources shipped with the package in the subdirectory named by LT_CONFIG_LTDL_DIR, they should pass this option to configure.

If the --with-included-ltdl is not passed at configure time, and an installed libltdl is not found13, then configure will exit immediately with an error that asks the user to either specify the location of an installed libltdl using the --with-ltdl-include and --with-ltdl-lib options, or to build with the libltdl sources shipped with the package by passing --with-included-ltdl.

If an installed libltdl is found, then LIBLTDL is set to the link flags needed to use it, and LTDLINCL to the preprocessor flags needed to find the installed headers, and LTDLDEPS will be empty. Note, however, that no version checking is performed. You should manually check for the libltdl features you need in configure.ac:

          LT_INIT([dlopen])
          LTDL_INIT
          
          # The lt_dladvise_init symbol was added with libtool-2.2
          if test "x$with_included_ltdl" != "xyes"; then
            save_CFLAGS="$CFLAGS"
            save_LDFLAGS="$LDFLAGS"
            CFLAGS="$CFLAGS $LTDLINCL"
            LDFLAGS="$LDFLAGS $LIBLTDL"
            AC_CHECK_LIB([ltdl], [lt_dladvise_init],
                          [],
                  [AC_MSG_ERROR([installed libltdl is too old])])
            LDFLAGS="$save_LDFLAGS"
            CFLAGS="$save_CFLAGS"
          fi

options may include no more than one of the following build modes depending on how you want your project to build libltdl: ‘nonrecursive’, ‘recursive’, or ‘subproject’. In order for libtoolize to detect this option correctly, if you supply one of these arguments, they must be given literally (i.e., macros or shell variables that expand to the correct ltdl mode will not work).

nonrecursive
This is how the Libtool project distribution builds the libltdl we ship and install. If you wish to use Automake to build libltdlwithout invoking a recursive make to descend into the libltdl subdirectory, then use this option. You will need to set your configuration up carefully to make this work properly, and you will need releases of Autoconf and Automake that supportsubdir-objects and LIBOBJDIR properly. In your configure.ac, add:
               AM_INIT_AUTOMAKE([subdir-objects])
               AC_CONFIG_HEADERS([config.h])
               LT_CONFIG_LTDL_DIR([libltdl])
               LT_INIT([dlopen])
               LTDL_INIT([nonrecursive])

You have to use a config header, but it may have a name different than config.h.

Also, add the following near the top of your Makefile.am:

               AM_CPPFLAGS =
               AM_LDFLAGS =
               
               BUILT_SOURCES =
               EXTRA_DIST =
               CLEANFILES =
               MOSTLYCLEANFILES =
               
               include_HEADERS =
               noinst_LTLIBRARIES =
               lib_LTLIBRARIES =
               EXTRA_LTLIBRARIES =
               
               include libltdl/Makefile.inc

Unless you build no other libraries from this Makefile.am, you will also need to change lib_LTLIBRARIES to assign with ‘+=’ so that the libltdl targets declared in Makefile.inc are not overwritten.

recursive
This build mode still requires that you use Automake, but (in contrast with ‘nonrecursive’) uses the more usual device of starting another make process in the libltdl subdirectory. To use this mode, you should add to your configure.ac:
               AM_INIT_AUTOMAKE
               AC_CONFIG_HEADERS([config.h])
               LT_CONFIG_LTDL_DIR([libltdl])
               LT_INIT([dlopen])
               LTDL_INIT([recursive])
               AC_CONFIG_FILES([libltdl/Makefile])

Again, you have to use a config header, but it may have a name different than config.h if you like.

Also, add this to your Makefile.am:

               SUBDIRS = libltdl

subproject
This mode is the default unless you explicitly add recursive or nonrecursive to your LTDL_INIT options; subproject is the only mode supported by previous releases of libltdl. Even if you do not use Autoconf in the parent project, then, in ‘subproject’ mode, still libltdl contains all the necessary files to configure and build itself – you just need to arrange for your build system to call libltdl/configure with appropriate options, and then run make in the libltdl subdirectory.

If you are using Autoconf and Automake, then you will need to add the following to your configure.ac:

               LT_CONFIG_LTDL_DIR([libltdl])
               LTDL_INIT

and to Makefile.am:

               SUBDIRS = libltdl

Aside from setting the libltdl build mode, there are other keywords that you can pass to LTDL_INIT to modify its behavior when --with-included-ltdl has been given:

convenience
This is the default unless you explicitly add installable to your LTDL_INIT options.

This keyword will cause options to be passed to the configure script in the subdirectory named by LT_CONFIG_LTDL_DIR in order to cause it to be built as a convenience library. If you're not using automake, you will need to define top_build_prefix,top_builddir, and top_srcdir in your makefile so that LIBLTDL, LTDLDEPS, and LTDLINCL expand correctly.

One advantage of the convenience library is that it is not installed, so the fact that you use libltdl will not be apparent to the user, and it won't overwrite a pre-installed version of libltdl the system might already have in the installation directory. On the other hand, if you want to upgrade libltdl for any reason (e.g. a bugfix) you'll have to recompile your package instead of just replacing the shared installed version of libltdl. However, if your programs or libraries are linked with other libraries that use such a pre-installed version of libltdl, you may get linker errors or run-time crashes. Another problem is that you cannot link the convenience library into more than one libtool library, then link a single program with those libraries, because you may get duplicate symbols. In general you can safely use the convenience library in programs that don't depend on other libraries that might use libltdl too.

installable
This keyword will pass options to the configure script in the subdirectory named by LT_CONFIG_LTDL_DIR in order to cause it to be built as an installable library. If you're not using automake, you will need to define top_build_prefix, top_builddir andtop_srcdir in your makefile so that LIBLTDL, LTDLDEPS, and LTDLINCL are expanded properly.

Be aware that you could overwrite another libltdl already installed to the same directory if you use this option.

Whatever method you use, ‘LTDL_INIT’ will define the shell variable LIBLTDL to the link flag that you should use to link with libltdl, the shell variable LTDLDEPS to the files that can be used as a dependency in Makefile rules, and the shell variable LTDLINCL to the preprocessor flag that you should use to compile programs that include ltdl.h. So, when you want to link a program with libltdl, be it a convenience, installed or installable library, just use ‘$(LTDLINCL)’ for preprocessing and compilation, and ‘$(LIBLTDL)’ for linking.

  • If your package is built using an installed version of libltdl, LIBLTDL will be set to the compiler flags needed to link against the installed library, LTDLDEPS will be empty, and LTDLINCL will be set to the compiler flags needed to find the libltdl header files.
  • If your package is built using the convenience libltdl, LIBLTDL and LTDLDEPS will be the pathname for the convenience version of libltdl (starting with ‘${top_builddir}/’ or ‘${top_build_prefix}’) and LTDLINCL will be -I followed by the directory that contains ltdl.h (starting with ‘${top_srcdir}/’).
  • If an installable version of the included libltdl is being built, its pathname starting with ‘${top_builddir}/’ or ‘${top_build_prefix}’, will be stored in LIBLTDL and LTDLDEPS, and LTDLINCL will be set just like in the case of convenience library.

You should probably also use the ‘dlopen’ option to LT_INIT in your configure.ac, otherwise libtool will assume no dlopening mechanism is supported, and revert to dlpreopening, which is probably not what you want. Avoid using the -static, -static-libtool-libs, or -all-static switches when linking programs with libltdl. This will not work on all platforms, because the dlopening functions may not be available for static linking.

The following example shows you how to embed an installable libltdl in your package. In order to use the convenience variant, just replace the LTDL_INIT option ‘installable’ with ‘convenience’. We assume that libltdl was embedded using ‘libtoolize --ltdl’.

configure.ac:

     ...
     # Name the subdirectory that contains libltdl sources
     LT_CONFIG_LTDL_DIR([libltdl])
     
     # Configure libtool with dlopen support if possible
     LT_INIT([dlopen])
     
     # Enable building of the installable libltdl library
     LTDL_INIT([installable])
     ...

Makefile.am:

     ...
     SUBDIRS = libltdl
     
     AM_CPPFLAGS = $(LTDLINCL)
     
     myprog_LDFLAGS = -export-dynamic
     myprog_LDADD = $(LIBLTDL) -dlopen self -dlopen foo1.la
     myprog_DEPENDENCIES = $(LTDLDEPS) foo1.la
     ...
— Macro: LTDL_INSTALLABLE
— Macro: AC_LIBLTDL_INSTALLABLE

These macros are deprecated, the ‘installable’ option to LTDL_INIT should be used instead.

— Macro: LTDL_CONVENIENCE
— Macro: AC_LIBLTDL_CONVENIENCE

These macros are deprecated, the ‘convenience’ option to LTDL_INIT should be used instead.


Next: , Previous: Using libltdl, Up: Top

12 Libtool's trace interface

This section describes macros whose sole purpose is to be traced using Autoconf's --trace option (see The Autoconf Manual) to query the Libtool configuration of a project. These macros are called by Libtool internals and should never be called by user code; they should only be traced.

— Macro: LT_SUPPORTED_TAG (tag)

This macro is called once for each language enabled in the package. Its only argument, tag, is the tag-name corresponding to the language (see Tags).

You can therefore retrieve the list of all tags enabled in a project using the following command:

          autoconf --trace 'LT_SUPPORTED_TAG:$1'

Next: , Previous: Trace interface, Up: Top

13 Frequently Asked Questions about libtool

This chapter covers some questions that often come up on the mailing lists.


Up: FAQ

13.1 Why does libtool strip link flags when creating a library?

When creating a shared library, but not when compiling or creating a program, libtool drops some flags from the command line provided by the user. This is done because flags unknown to libtool may interfere with library creation or require additional support from libtool, and because omitting flags is usually the conservative choice for a successful build.

If you encounter flags that you think are useful to pass, as a work-around you can prepend flags with -Wc, or -Xcompiler to allow them to be passed through to the compiler driver (see Link mode). Another possibility is to add flags already to the compiler command at configure run time:

     ./configure CC='gcc -m64'

If you think libtool should let some flag through by default, here's how you can test such an inclusion: grab the Libtool development tree, edit the ltmain.m4sh file in the libltdl/config subdirectory to pass through the flag (search for ‘Flags to be passed through’), re-bootstrap and build with the flags in question added to LDFLAGS, CFLAGS, CXXFLAGS, etc. on the configure command line as appropriate. Run the testsuite as described in the README file and report results to the Libtool bug reporting address bug-libtool@gnu.org.


Next: , Previous: FAQ, Up: Top

14 Troubleshooting

Libtool is under constant development, changing to remain up-to-date with modern operating systems. If libtool doesn't work the way you think it should on your platform, you should read this chapter to help determine what the problem is, and how to resolve it.

14.1 The libtool test suite

Libtool comes with two integrated sets of tests to check that your build is sane, that test its capabilities, and report obvious bugs in the libtool program. These tests, too, are constantly evolving, based on past problems with libtool, and known deficiencies in other operating systems.

As described in the README file, you may run make -k check after you have built libtool (possibly before you install it) in order to make sure that it meets basic functional requirements.

14.1.1 Description of test suite

Here is a list of the current programs in the old test suite, and what they test for:

cdemo-conf.test
cdemo-make.test
cdemo-exec.test
cdemo-static.test
cdemo-static-make.test
cdemo-static-exec.test
cdemo-shared.test
cdemo-shared-make.test
cdemo-shared-exec.test
cdemo-undef.test
cdemo-undef-make.test
cdemo-undef-exec.test
These programs check to see that the tests/cdemo subdirectory of the libtool distribution can be configured and built correctly.

The tests/cdemo subdirectory contains a demonstration of libtool convenience libraries, a mechanism that allows build-time static libraries to be created, in a way that their components can be later linked into programs or other libraries, even shared ones.

The tests matching cdemo-*make.test and cdemo-*exec.test are executed three times, under three different libtool configurations: cdemo-conf.test configures cdemo/libtool to build both static and shared libraries (the default for platforms that support both), cdemo-static.testbuilds only static libraries (‘--disable-shared’), and cdemo-shared.test builds only shared libraries (‘--disable-static’).

The test cdemo-undef.test tests the generation of shared libraries with undefined symbols on systems that allow this.

demo-conf.test
demo-make.test
demo-exec.test
demo-inst.test
demo-unst.test
demo-static.test
demo-static-make.test
demo-static-exec.test
demo-static-inst.test
demo-static-unst.test
demo-shared.test
demo-shared-make.test
demo-shared-exec.test
demo-shared-inst.test
demo-shared-unst.test
demo-nofast.test
demo-nofast-make.test
demo-nofast-exec.test
demo-nofast-inst.test
demo-nofast-unst.test
demo-pic.test
demo-pic-make.test
demo-pic-exec.test
demo-nopic.test
demo-nopic-make.test
demo-nopic-exec.test
These programs check to see that the tests/demo subdirectory of the libtool distribution can be configured, built, installed, and uninstalled correctly.

The tests/demo subdirectory contains a demonstration of a trivial package that uses libtool. The tests matching demo-*make.test, demo-*exec.test, demo-*inst.test and demo-*unst.test are executed four times, under four different libtool configurations: demo-conf.test configuresdemo/libtool to build both static and shared libraries, demo-static.test builds only static libraries (--disable-shared), and demo-shared.test builds only shared libraries (--disable-static). demo-nofast.test configures demo/libtool to disable the fast-install mode (--enable-fast-install=no). demo-pic.test configures demo/libtool to prefer building PIC code (--with-pic), demo-nopic.test to prefer non-PIC code (--without-pic).

demo-deplibs.test
Many systems cannot link static libraries into shared libraries. libtool uses a deplibs_check_method to prevent such cases. This tests checks whether libtool's deplibs_check_method works properly.
demo-hardcode.test
On all systems with shared libraries, the location of the library can be encoded in executables that are linked against it see Linking executables. This test checks the conditions under which your system linker hardcodes the library location, and guarantees that they correspond to libtool's own notion of how your linker behaves.
demo-relink.test
depdemo-relink.test
These tests check whether variable shlibpath_overrides_runpath is properly set. If the test fails, it will indicate what the variable should have been set to.
demo-noinst-link.test
Checks whether libtool will not try to link with a previously installed version of a library when it should be linking with a just-built one.
depdemo-conf.test
depdemo-make.test
depdemo-exec.test
depdemo-inst.test
depdemo-unst.test
depdemo-static.test
depdemo-static-make.test
depdemo-static-exec.test
depdemo-static-inst.test
depdemo-static-unst.test
depdemo-shared.test
depdemo-shared-make.test
depdemo-shared-exec.test
depdemo-shared-inst.test
depdemo-shared-unst.test
depdemo-nofast.test
depdemo-nofast-make.test
depdemo-nofast-exec.test
depdemo-nofast-inst.test
depdemo-nofast-unst.test
These programs check to see that the tests/depdemo subdirectory of the libtool distribution can be configured, built, installed, and uninstalled correctly.

The tests/depdemo subdirectory contains a demonstration of inter-library dependencies with libtool. The test programs link some interdependent libraries.

The tests matching depdemo-*make.test, depdemo-*exec.test, depdemo-*inst.test and depdemo-*unst.test are executed four times, under four different libtool configurations: depdemo-conf.test configures depdemo/libtool to build both static and shared libraries, depdemo-static.testbuilds only static libraries (--disable-shared), and depdemo-shared.test builds only shared libraries (--disable-static). depdemo-nofast.test configuresdepdemo/libtool to disable the fast-install mode (--enable-fast-install=no).

mdemo-conf.test
mdemo-make.test
mdemo-exec.test
mdemo-inst.test
mdemo-unst.test
mdemo-static.test
mdemo-static-make.test
mdemo-static-exec.test
mdemo-static-inst.test
mdemo-static-unst.test
mdemo-shared.test
mdemo-shared-make.test
mdemo-shared-exec.test
mdemo-shared-inst.test
mdemo-shared-unst.test
These programs check to see that the tests/mdemo subdirectory of the libtool distribution can be configured, built, installed, and uninstalled correctly.

The tests/mdemo subdirectory contains a demonstration of a package that uses libtool and the system independent dlopen wrapperlibltdl to load modules. The library libltdl provides a dlopen wrapper for various platforms (POSIX) including support for dlpreopened modules (see Dlpreopening).

The tests matching mdemo-*make.test, mdemo-*exec.test, mdemo-*inst.test and mdemo-*unst.test are executed three times, under three different libtool configurations: mdemo-conf.test configures mdemo/libtool to build both static and shared libraries, mdemo-static.test builds only static libraries (--disable-shared), and mdemo-shared.test builds only shared libraries (--disable-static).

mdemo-dryrun.test
This test checks whether libtool's --dry-run mode works properly.
mdemo2-conf.test
mdemo2-exec.test
mdemo2-make.test
These programs check to see that the tests/mdemo2 subdirectory of the libtool distribution can be configured, built, and executed correctly.

The tests/mdemo2 directory contains a demonstration of a package that attempts to link with a library (from the tests/mdemo directory) that itself does dlopening of libtool modules.

link.test
This test guarantees that linking directly against a non-libtool static library works properly.
link-2.test
This test makes sure that files ending in .lo are never linked directly into a program file.
nomode.test
Check whether we can actually get help for libtool.
objectlist.test
Check that a nonexistent objectlist file is properly detected.
pdemo-conf.test
pdemo-make.test
pdemo-exec.test
pdemo-inst.test
These programs check to see that the tests/pdemo subdirectory of the libtool distribution can be configured, built, and executed correctly.

The pdemo-conf.test lowers the max_cmd_len variable in the generated libtool script to test the measures to evade command line length limitations.

quote.test
This program checks libtool's metacharacter quoting.
sh.test
Checks for some nonportable or dubious or undesired shell constructs in shell scripts.
suffix.test
When other programming languages are used with libtool (see Other languages), the source files may end in suffixes other than .c. This test validates that libtool can handle suffixes for all the file types that it supports, and that it fails when the suffix is invalid.
tagdemo-conf.test
tagdemo-make.test
tagdemo-exec.test
tagdemo-static.test
tagdemo-static-make.test
tagdemo-static-exec.test
tagdemo-shared.test
tagdemo-shared-make.test
tagdemo-shared-exec.test
tagdemo-undef.test
tagdemo-undef-make.test
tagdemo-undef-exec.test
These programs check to see that the tests/tagdemo subdirectory of the libtool distribution can be configured, built, and executed correctly.

The tests/tagdemo directory contains a demonstration of a package that uses libtool's multi-language support through configuration tags. It generates a library from C++ sources, which is then linked to a C++ program.

f77demo-conf.test
f77demo-make.test
f77demo-exec.test
f77demo-static.test
f77demo-static-make.test
f77demo-static-exec.test
f77demo-shared.test
f77demo-shared-make.test
f77demo-shared-exec.test
These programs check to see that the tests/f77demo subdirectory of the libtool distribution can be configured, built, and executed correctly.

The tests/f77demo tests test Fortran 77 support in libtool by creating libraries from Fortran 77 sources, and mixed Fortran and C sources, and a Fortran 77 program to use the former library, and a C program to use the latter library.

fcdemo-conf.test
fcdemo-make.test
fcdemo-exec.test
fcdemo-static.test
fcdemo-static-make.test
fcdemo-static-exec.test
fcdemo-shared.test
fcdemo-shared-make.test
fcdemo-shared-exec.test
These programs check to see that the tests/fcdemo subdirectory of the libtool distribution can be configured, built, and executed correctly.

The tests/fcdemo is similar to the tests/f77demo directory, except that Fortran 90 is used in combination with the ‘FC’ interface provided by Autoconf and Automake.

The new, Autotest-based test suite uses keywords to classify certain test groups:

CXX
F77
FC
GCJ
The test group exercises one of these libtool language tags.
autoconf
automake
These keywords denote that the respective external program is needed by the test group. The tests are typically skipped if the program is not installed. The ‘automake’ keyword may also denote use of the aclocal program.
interactive
This test group may require user interaction on some systems. Typically, this means closing a popup window about a DLL load error on Windows.
libltdl
Denote that the libltdl library is exercised by the test group.
libtool
libtoolize
Denote that the libtool or libtoolize scripts are exercised by the test group, respectively.
recursive
Denote that this test group may recursively re-invoke the test suite itself, with changed settings and maybe a changed libtool script. You may use the INNER_TESTSUITEFLAGS variable to pass additional settings to this recursive invocation. Typically, recursive invocations delimit the set of tests with another keyword, for example by passing -k libtool right before the expansion of the INNER_TESTSUITEFLAGSvariable (without an intervening space, so you get the chance for further delimitation).

Test groups with the keyword ‘recursive’ should not be denoted with keywords, in order to avoid infinite recursion. As a consequence, recursive test groups themselves should never require user interaction, while the test groups they invoke may do so.

There is a convenience target ‘check-noninteractive’ that runs all tests from both test suites that do not cause user interaction on Windows. Conversely, the target ‘check-interactive’ runs the complement of tests and might require closing popup windows about DLL load errors on Windows.

14.1.2 When tests fail

When the tests in the old test suite are run via make check, output is caught in per-test tests/test-name.log files and summarized in the test-suite.log file. The exit status of each program tells the Makefile whether or not the test succeeded.

If a test fails, it means that there is either a programming error in libtool, or in the test program itself.

To investigate a particular test, you may run it directly, as you would a normal program. When the test is invoked in this way, it produces output that may be useful in determining what the problem is.

The new, Autotest-based test suite produces as output a file tests/testsuite.log which contains information about failed tests.

You can pass options to the test suite through the make variable TESTSUITEFLAGS (see The Autoconf Manual).

14.2 Reporting bugs

If you think you have discovered a bug in libtool, you should think twice: the libtool maintainer is notorious for passing the buck (or maybe that should be “passing the bug”). Libtool was invented to fix known deficiencies in shared library implementations, so, in a way, most of the bugs in libtool are actually bugs in other operating systems. However, the libtool maintainer would definitely be happy to add support for somebody else's buggy operating system. [I wish there was a good way to do winking smiley-faces in Texinfo.]

Genuine bugs in libtool include problems with shell script portability, documentation errors, and failures in the test suite (see Libtool test suite).

First, check the documentation and help screens to make sure that the behaviour you think is a problem is not already mentioned as a feature.

Then, you should read the Emacs guide to reporting bugs (see Reporting Bugs). Some of the details listed there are specific to Emacs, but the principle behind them is a general one.

Finally, send a bug report to the Libtool bug reporting address bug-libtool@gnu.org with any appropriate facts, such as test suite output (see When tests fail), all the details needed to reproduce the bug, and a brief description of why you think the behaviour is a bug. Be sure to include the word “libtool” in the subject line, as well as the version number you are using (which can be found by typing libtool --version).

15 Maintenance notes for libtool

This chapter contains information that the libtool maintainer finds important. It will be of no use to you unless you are considering porting libtool to new systems, or writing your own libtool.

15.1 Porting libtool to new systems

Before you embark on porting libtool to an unsupported system, it is worthwhile to send e-mail to the Libtool mailing list libtool@gnu.org, to make sure that you are not duplicating existing work.

If you find that any porting documentation is missing, please complain! Complaints with patches and improvements to the documentation, or to libtool itself, are more than welcome.

15.1.1 Information sources

Once it is clear that a new port is necessary, you'll generally need the following information:

canonical system name
You need the output of config.guess for this system, so that you can make changes to the libtool configuration process without affecting other systems.
man pages for ld and cc
These generally describe what flags are used to generate PIC, to create shared libraries, and to link against only static libraries. You may need to follow some cross references to find the information that is required.
man pages for ld.so, rtld, or equivalent
These are a valuable resource for understanding how shared libraries are loaded on the system.
man page for ldconfig, or equivalent
This page usually describes how to install shared libraries.
output from ls -l /lib /usr/lib
This shows the naming convention for shared libraries on the system, including which names should be symbolic links.
any additional documentation
Some systems have special documentation on how to build and install shared libraries.

If you know how to program the Bourne shell, then you can complete the port yourself; otherwise, you'll have to find somebody with the relevant skills who will do the work. People on the libtool mailing list are usually willing to volunteer to help you with new ports, so you can send the information to them.

To do the port yourself, you'll definitely need to modify the libtool.m4 macros in order to make platform-specific changes to the configuration process. You should search that file for the PORTME keyword, which will give you some hints on what you'll need to change. In general, all that is involved is modifying the appropriate configuration variables (see libtool script contents).

Your best bet is to find an already-supported system that is similar to yours, and make your changes based on that. In some cases, however, your system will differ significantly from every other supported system, and it may be necessary to add new configuration variables, and modify the ltmain.in script accordingly. Be sure to write to the mailing list before you make changes to ltmain.in, since they may have advice on the most effective way of accomplishing what you want.

15.1.2 Porting inter-library dependencies support

Since version 1.2c, libtool has re-introduced the ability to do inter-library dependency on some platforms, thanks to a patch by Toshio Kuratomi badger@prtr-13.ucsc.edu. Here's a shortened version of the message that contained his patch:

The basic architecture is this: in libtool.m4, the person who writes libtool makes sure ‘$deplibs’ is included in ‘$archive_cmds’ somewhere and also sets the variable ‘$deplibs_check_method’, and maybe ‘$file_magic_cmd’ when ‘deplibs_check_method’ is file_magic.

deplibs_check_method’ can be one of five things:

file_magic [regex]
looks in the library link path for libraries that have the right libname. Then it runs ‘$file_magic_cmd’ on the library and checks for a match against the extended regular expression regex. When file_magic_test_file is set by libtool.m4, it is used as an argument to ‘$file_magic_cmd’ in order to verify whether the regular expression matches its output, and warn the user otherwise.
test_compile
just checks whether it is possible to link a program out of a list of libraries, and checks which of those are listed in the output of ldd. It is currently unused, and will probably be dropped in the future.
pass_all
will pass everything without any checking. This may work on platforms in which code is position-independent by default and inter-library dependencies are properly supported by the dynamic linker, for example, on DEC OSF/1 3 and 4.
none
It causes deplibs to be reassigned ‘deplibs=""’. That way ‘archive_cmds’ can contain deplibs on all platforms, but not have deplibs used unless needed.
unknown
is the default for all systems unless overridden in libtool.m4. It is the same as ‘none’, but it documents that we really don't know what the correct value should be, and we welcome patches that improve it.

Then in ltmain.in we have the real workhorse: a little initialization and postprocessing (to setup/release variables for use with eval echo libname_spec etc.) and a case statement that decides the method that is being used. This is the real code... I wish I could condense it a little more, but I don't think I can without function calls. I've mostly optimized it (moved things out of loops, etc.) but there is probably some fat left. I thought I should stop while I was ahead, work on whatever bugs you discover, etc. before thinking about more than obvious optimizations.


Next: , Previous: New ports, Up: Maintaining

15.2 Tested platforms

This table describes when libtool was last known to be tested on platforms where it claims to support shared libraries:

     -------------------------------------------------------
     canonical host name          compiler  libtool results
       (tools versions)                     release
     -------------------------------------------------------
     alpha-dec-osf5.1		cc	 1.3e	  ok (1.910)
     alpha-dec-osf4.0f               gcc      1.3e     ok (1.910)
     alpha-dec-osf4.0f               cc       1.3e     ok (1.910)
     alpha-dec-osf3.2                gcc      0.8      ok
     alpha-dec-osf3.2                cc       0.8      ok
     alpha-dec-osf2.1                gcc      1.2f     NS
     alpha*-unknown-linux-gnu        gcc      1.3b     ok
       (egcs-1.1.2, GNU ld 2.9.1.0.23)
     hppa2.0w-hp-hpux11.00           cc       1.2f     ok
     hppa2.0-hp-hpux10.20            cc       1.3.2    ok
     hppa1.1-hp-hpux10.20            gcc      1.2f     ok
     hppa1.1-hp-hpux10.20            cc       1.3c     ok (1.821)
     hppa1.1-hp-hpux10.10            gcc      1.2f     ok
     hppa1.1-hp-hpux10.10            cc       1.2f     ok
     hppa1.1-hp-hpux9.07             gcc      1.2f     ok
     hppa1.1-hp-hpux9.07             cc       1.2f     ok
     hppa1.1-hp-hpux9.05             gcc      1.2f     ok
     hppa1.1-hp-hpux9.05             cc       1.2f     ok
     hppa1.1-hp-hpux9.01             gcc      1.2f     ok
     hppa1.1-hp-hpux9.01             cc       1.2f     ok
     i*86-*-beos                     gcc      1.2f     ok
     i*86-*-bsdi4.0.1                gcc      1.3c     ok
       (gcc-2.7.2.1)
     i*86-*-bsdi4.0                  gcc      1.2f     ok
     i*86-*-bsdi3.1                  gcc      1.2e     NS
     i*86-*-bsdi3.0                  gcc      1.2e     NS
     i*86-*-bsdi2.1                  gcc      1.2e     NS
     i*86-pc-cygwin                  gcc      1.3b     NS
       (egcs-1.1 stock b20.1 compiler)
     i*86-*-dguxR4.20MU01            gcc      1.2      ok
     i*86-*-freebsd4.3		gcc      1.3e     ok (1.912)
     i*86-*-freebsdelf4.0            gcc      1.3c     ok
       (egcs-1.1.2)
     i*86-*-freebsdelf3.2            gcc      1.3c     ok
       (gcc-2.7.2.1)
     i*86-*-freebsdelf3.1            gcc      1.3c     ok
       (gcc-2.7.2.1)
     i*86-*-freebsdelf3.0            gcc      1.3c     ok
     i*86-*-freebsd3.0               gcc      1.2e     ok
     i*86-*-freebsd2.2.8             gcc      1.3c     ok
       (gcc-2.7.2.1)
     i*86-*-freebsd2.2.6             gcc      1.3b     ok
       (egcs-1.1 & gcc-2.7.2.1, native ld)
     i*86-*-freebsd2.1.5             gcc      0.5      ok
     i*86-*-netbsd1.5                gcc      1.3e     ok (1.901)
       (egcs-1.1.2)
     i*86-*-netbsd1.4                gcc      1.3c     ok
       (egcs-1.1.1)
     i*86-*-netbsd1.4.3A             gcc      1.3e     ok (1.901)
     i*86-*-netbsd1.3.3              gcc      1.3c     ok
       (gcc-2.7.2.2+myc2)
     i*86-*-netbsd1.3.2              gcc      1.2e     ok
     i*86-*-netbsd1.3I               gcc      1.2e     ok
       (egcs 1.1?)
     i*86-*-netbsd1.2                gcc      0.9g     ok
     i*86-*-linux-gnu		gcc	 1.3e	  ok (1.901)
       (Red Hat 7.0, gcc "2.96")
     i*86-*-linux-gnu		gcc	 1.3e	  ok (1.911)
       (SuSE 7.0, gcc 2.95.2)
     i*86-*-linux-gnulibc1           gcc      1.2f     ok
     i*86-*-openbsd2.5               gcc      1.3c     ok
       (gcc-2.8.1)
     i*86-*-openbsd2.4               gcc      1.3c     ok
       (gcc-2.8.1)
     i*86-*-solaris2.7               gcc      1.3b     ok
       (egcs-1.1.2, native ld)
     i*86-*-solaris2.6               gcc      1.2f     ok
     i*86-*-solaris2.5.1             gcc      1.2f     ok
     i*86-ncr-sysv4.3.03             gcc      1.2f     ok
     i*86-ncr-sysv4.3.03             cc       1.2e     ok
       (cc -Hnocopyr)
     i*86-pc-sco3.2v5.0.5		cc	 1.3c	  ok
     i*86-pc-sco3.2v5.0.5		gcc	 1.3c	  ok
       (gcc 95q4c)
     i*86-pc-sco3.2v5.0.5		gcc	 1.3c	  ok
       (egcs-1.1.2)
     i*86-sco-sysv5uw7.1.1		gcc	 1.3e	  ok (1.901)
       (gcc-2.95.2, SCO linker)
     i*86-UnixWare7.1.0-sysv5	cc	 1.3c	  ok
     i*86-UnixWare7.1.0-sysv5	gcc	 1.3c	  ok
       (egcs-1.1.1)
     m68k-next-nextstep3             gcc      1.2f     NS
     m68k-sun-sunos4.1.1             gcc      1.2f     NS
       (gcc-2.5.7)
     m88k-dg-dguxR4.12TMU01          gcc      1.2      ok
     m88k-motorola-sysv4             gcc      1.3      ok
       (egcs-1.1.2)
     mips-sgi-irix6.5                gcc      1.2f     ok
       (gcc-2.8.1)
     mips-sgi-irix6.4                gcc      1.2f     ok
     mips-sgi-irix6.3                gcc      1.3b     ok
       (egcs-1.1.2, native ld)
     mips-sgi-irix6.3                cc       1.3b     ok
       (cc 7.0)
     mips-sgi-irix6.2                gcc      1.2f     ok
     mips-sgi-irix6.2                cc       0.9      ok
     mips-sgi-irix5.3                gcc      1.2f     ok
       (egcs-1.1.1)
     mips-sgi-irix5.3                gcc      1.2f     NS
       (gcc-2.6.3)
     mips-sgi-irix5.3                cc       0.8      ok
     mips-sgi-irix5.2                gcc      1.3b     ok
       (egcs-1.1.2, native ld)
     mips-sgi-irix5.2                cc       1.3b     ok
       (cc 3.18)
     mips-sni-sysv4			cc       1.3.5    ok
       (Siemens C-compiler)
     mips-sni-sysv4			gcc      1.3.5    ok
       (gcc-2.7.2.3, GNU assembler 2.8.1, native ld)
     mipsel-unknown-openbsd2.1       gcc      1.0      ok
     powerpc-apple-darwin6.4         gcc      1.5      ok
     (apple dev tools released 12/2002)
     powerpc-ibm-aix4.3.1.0          gcc      1.2f     ok
       (egcs-1.1.1)
     powerpc-ibm-aix4.2.1.0          gcc      1.2f     ok
       (egcs-1.1.1)
     powerpc-ibm-aix4.1.5.0          gcc      1.2f     ok
       (egcs-1.1.1)
     powerpc-ibm-aix4.1.5.0          gcc      1.2f     NS
       (gcc-2.8.1)
     powerpc-ibm-aix4.1.4.0          gcc      1.0      ok
     powerpc-ibm-aix4.1.4.0          xlc      1.0i     ok
     rs6000-ibm-aix4.1.5.0           gcc      1.2f     ok
       (gcc-2.7.2)
     rs6000-ibm-aix4.1.4.0           gcc      1.2f     ok
       (gcc-2.7.2)
     rs6000-ibm-aix3.2.5             gcc      1.0i     ok
     rs6000-ibm-aix3.2.5             xlc      1.0i     ok
     sparc-sun-solaris2.8		gcc	 1.3e	  ok (1.913)
       (gcc-2.95.3 & native ld)
     sparc-sun-solaris2.7            gcc      1.3e     ok (1.913)
       (gcc-2.95.3 & native ld)
     sparc-sun-solaris2.6            gcc      1.3e     ok (1.913)
       (gcc-2.95.3 & native ld)
     sparc-sun-solaris2.5.1          gcc      1.3e     ok (1.911)
     sparc-sun-solaris2.5            gcc      1.3b     ok
       (egcs-1.1.2, GNU ld 2.9.1 & native ld)
     sparc-sun-solaris2.5            cc       1.3b     ok
       (SC 3.0.1)
     sparc-sun-solaris2.4            gcc      1.0a     ok
     sparc-sun-solaris2.4            cc       1.0a     ok
     sparc-sun-solaris2.3            gcc      1.2f     ok
     sparc-sun-sunos4.1.4            gcc      1.2f     ok
     sparc-sun-sunos4.1.4            cc       1.0f     ok
     sparc-sun-sunos4.1.3_U1         gcc      1.2f     ok
     sparc-sun-sunos4.1.3C           gcc      1.2f     ok
     sparc-sun-sunos4.1.3            gcc      1.3b     ok
       (egcs-1.1.2, GNU ld 2.9.1 & native ld)
     sparc-sun-sunos4.1.3            cc       1.3b     ok
     sparc-unknown-bsdi4.0           gcc      1.2c     ok
     sparc-unknown-linux-gnulibc1    gcc      1.2f     ok
     sparc-unknown-linux-gnu         gcc      1.3b     ok
       (egcs-1.1.2, GNU ld 2.9.1.0.23)
     sparc64-unknown-linux-gnu       gcc      1.2f     ok
     
     Notes:
     - "ok" means "all tests passed".
     - "NS" means "Not Shared", but OK for static libraries

Note: The vendor-distributed HP-UX sed(1) programs are horribly broken, and cannot handle libtool's requirements, so users may report unusual problems. There is no workaround except to install a working sed (such as GNU sed) on these systems.

Note: The vendor-distributed NCR MP-RAS cc programs emits copyright on standard error that confuse tests on size of conftest.err. The workaround is to specify CC when run configure with CC='cc -Hnocopyr'.

15.3 Platform quirks

This section is dedicated to the sanity of the libtool maintainers. It describes the programs that libtool uses, how they vary from system to system, and how to test for them.

Because libtool is a shell script, it can be difficult to understand just by reading it from top to bottom. This section helps show why libtool does things a certain way. Combined with the scripts themselves, you should have a better sense of how to improve libtool, or write your own.

15.3.1 References

The following is a list of valuable documentation references:

15.3.2 Compilers

The only compiler characteristics that affect libtool are the flags needed (if any) to generate PIC objects. In general, if a C compiler supports certain PIC flags, then any derivative compilers support the same flags. Until there are some noteworthy exceptions to this rule, this section will document only C compilers.

The following C compilers have standard command line options, regardless of the platform:

gcc
This is the GNU C compiler, which is also the system compiler for many free operating systems (FreeBSD, GNU/Hurd, GNU/Linux, Lites, NetBSD, and OpenBSD, to name a few).

The -fpic or -fPIC flags can be used to generate position-independent code. -fPIC is guaranteed to generate working code, but the code is slower on m68k, m88k, and Sparc chips. However, using -fpic on those chips imposes arbitrary size limits on the shared libraries.

The rest of this subsection lists compilers by the operating system that they are bundled with:

aix3*
aix4*
Most AIX compilers have no PIC flags, since AIX (with the exception of AIX for IA-64) runs on PowerPC and RS/6000 chips. 14 
hpux10*
Use ‘+Z’ to generate PIC.
osf3*
Digital/UNIX 3.x does not have PIC flags, at least not on the PowerPC platform.
solaris2*
Use -KPIC to generate PIC.
sunos4*
Use -PIC to generate PIC.

15.3.3 Reloadable objects

On all known systems, a reloadable object can be created by running ld -r -o output.o input1.o input2.o. This reloadable object may be treated as exactly equivalent to other objects.

15.3.4 Multiple dependencies

On most modern platforms the order in which dependent libraries are listed has no effect on object generation. In theory, there are platforms that require libraries that provide missing symbols to other libraries to be listed after those libraries whose symbols they provide.

Particularly, if a pair of static archives each resolve some of the other's symbols, it might be necessary to list one of those archives both before and after the other one. Libtool does not currently cope with this situation well, since duplicate libraries are removed from the link line by default. Libtool provides the command line option --preserve-dup-deps to preserve all duplicate dependencies in cases where it is necessary.

15.3.5 Archivers

On all known systems, building a static library can be accomplished by running ar cru libname.a obj1.o obj2.o ..., where the .a file is the output library, and each .o file is an object file.

On all known systems, if there is a program named ranlib, then it must be used to “bless” the created library before linking against it, with the ranlib libname.a command. Some systems, like Irix, use the ar ts command, instead.

15.3.6 Cross compiling

Most build systems support the ability to compile libraries and applications on one platform for use on a different platform, provided a compiler capable of generating the appropriate output is available. In such cross compiling scenarios, the platform on which the libraries or applications are compiled is called the build platform, while the platform on which the libraries or applications are intended to be used or executed is called the host platform. The GNU Build System, of which libtool is a part, supports cross compiling via arguments passed to the configure script: --build=... and --host=.... However, when the build platform and host platform are very different, libtool is required to make certain accommodations to support these scenarios.

In most cases, because the build platform and host platform differ, the cross-compiled libraries and executables can't be executed or tested on the build platform where they were compiled. The testsuites of most build systems will often skip any tests that involve executing such foreign executables when cross-compiling. However, if the build platform and host platform are sufficiently similar, it is often possible to run cross-compiled applications. Libtool's own testsuite often attempts to execute cross-compiled tests, but will mark any failures as skipped since the failure might simply be due to the differences between the two platforms.

In addition to cases where the host platform and build platform are extremely similar (e.g. ‘i586-pc-linux-gnu’ and ‘i686-pc-linux-gnu’), there is another case in which cross-compiled host applications may be executed on the build platform. This is possible when the build platform supports an emulation or API-enhanced environment for the host platform. One example of this situation would be if the build platform were MinGW, and the host platform were Cygwin (or vice versa). Both of these platforms can actually operate within a single Windows instance, so Cygwin applications can be launched from a MinGW context, and vice versa—provided certain care is taken. Another example would be if the build platform were GNU/Linux on an x86 32bit processor, and the host platform were MinGW. In this situation, the Wine environment can be used to launch Windows applications from the GNU/Linux operating system; again, provided certain care is taken.

One particular issue occurs when a Windows platform such as MinGW, Cygwin, or MSYS is the host or build platform, while the other platform is a Unix-style system. In these cases, there are often conflicts between the format of the file names and paths expected within host platform libraries and executables, and those employed on the build platform.

This situation is best described using a concrete example: suppose the build platform is GNU/Linux with canonical triplet ‘i686-pc-linux-gnu’. Suppose further that the host platform is MinGW with canonical triplet ‘i586-pc-mingw32’. On the GNU/Linux platform there is a cross compiler following the usual naming conventions of such compilers, where the compiler name is prefixed by the host canonical triplet (or suitable alias). (For more information concerning canonical triplets and platform aliases, see Specifying Target Triplets and Canonicalizing) In this case, the C compiler is named ‘i586-pc-mingw32-gcc’.

As described in Wrapper executables, for the MinGW host platform libtool uses a wrapper executable to set various environment variables before launching the actual program executable. Like the program executable, the wrapper executable is cross-compiled for the host platform (that is, for MinGW). As described above, ordinarily a host platform executable cannot be executed on the build platform, but in this case the Wine environment could be used to launch the MinGW application from GNU/Linux. However, the wrapper executable, as a host platform (MinGW) application, must set the PATH variable so that the true application's dependent libraries can be located—but the contents of the PATH variable must be structured for MinGW. Libtool must use the Wine file name mapping facilities to determine the correct value so that the wrapper executable can set the PATH variable to point to the correct location.

For example, suppose we are compiling an application in /var/tmp on GNU/Linux, using separate source code and build directories:

     
/var/tmp/foo-1.2.3/app/(application source code)
/var/tmp/foo-1.2.3/lib/(library source code)
/var/tmp/BUILD/app/(application build objects here)
/var/tmp/BUILD/lib/(library build objects here)

Since the library will be built in /var/tmp/BUILD/lib, the wrapper executable (which will be in /var/tmp/BUILD/app) must add that directory toPATH (actually, it must add the directory named objdir under /var/tmp/BUILD/lib, but we'll ignore that detail for now). However, Windows does not have a concept of Unix-style file or directory names such as /var/tmp/BUILD/lib. Therefore, Wine provides a mapping from Windows file names such as C:\Program Files to specific Unix-style file names. Wine also provides a utility that can be used to map Unix-style file names to Windows file names.

In this case, the wrapper executable should actually add the value

     Z:\var\tmp\BUILD\lib

to the PATH. libtool contains support for path conversions of this type, for a certain limited set of build and host platform combinations. In this case, libtool will invoke Wine's winepath utility to ensure that the correct PATH value is used. For more information, see see File name conversion.

15.3.7 File name conversion

In certain situations, libtool must convert file names and paths between formats appropriate to different platforms. Usually this occurs when cross-compiling, and affects only the ability to launch host platform executables on the build platform using an emulation or API-enhancement environment such as Wine. Failure to convert paths (see File Name Conversion Failure) will cause a warning to be issued, but rarely causes the build to fail—and should have no affect on the compiled products, once installed properly on the host platform. For more information, see Cross compiling.

However, file name conversion may also occur in another scenario: when using a Unix emulation system on Windows (such as Cygwin or MSYS), combined with a native Windows compiler such as MinGW or MSVC. Only a limited set of such scenarios are currently supported; in other cases file name conversion is skipped. The lack of file name conversion usually means that uninstalled executables can't be launched, but only rarely causes the build to fail (see File Name Conversion Failure).

libtool supports file name conversion in the following scenarios:

build platformhost platformNotes
MinGW (MSYS)MinGW (Windows)see Native MinGW File Name Conversion
CygwinMinGW (Windows)see Cygwin/Windows File Name Conversion
Unix + WineMinGW (Windows)Requires Wine. see Unix/Windows File Name Conversion
MinGW (MSYS)CygwinRequires LT_CYGPATH. see LT_CYGPATH. Provided for testing purposes only.
Unix + WineCygwinRequires both Wine and LT_CYGPATH, but does not yet work with Cygwin 1.7.7 and Wine-1.2. See see Unix/Windows File Name Conversion and see LT_CYGPATH.
15.3.7.1 File Name Conversion Failure

In most cases, file name conversion is not needed or attempted. However, when libtool detects that a specific combination of build and host platform does require file name conversion, it is possible that the conversion may fail. In these cases, you may see a warning such as the following:

     Could not determine the host file name corresponding to
       `... a file name ...'
     Continuing, but uninstalled executables may not work.

or

     Could not determine the host path corresponding to
       `... a path ...'
     Continuing, but uninstalled executables may not work.

This should not cause the build to fail. At worst, it means that the wrapper executable will specify file names or paths appropriate for the build platform. Since those are not appropriate for the host platform, the uninstalled executables would not operate correctly, even when the wrapper executable is launched via the appropriate emulation or API-enhancement (e.g. Wine). Simply install the executables on the host platform, and execute them there.

15.3.7.2 Native MinGW File Name Conversion

MSYS is a Unix emulation environment for Windows, and is specifically designed such that in normal usage it pretends to be MinGW or native Windows, but understands Unix-style file names and paths, and supports standard Unix tools and shells. Thus, “native” MinGW builds are actually an odd sort of cross-compile, from an MSYS Unix emulation environment “pretending” to be MinGW, to actual native Windows.

When an MSYS shell launches a native Windows executable (as opposed to other MSYS executables), it uses a system of heuristics to detect any command-line arguments that contain file names or paths. It automatically converts these file names from the MSYS (Unix-like) format, to the corresponding Windows file name, before launching the executable. However, this auto-conversion facility is only available when using the MSYS runtime library. The wrapper executable itself is a MinGW application (that is, it does not use the MSYS runtime library). The wrapper executable must set PATH to, and call _spawnv with, values that have already been converted from MSYS format to Windows. Thus, when libtool writes the source code for the wrapper executable, it must manually convert MSYS paths to Windows format, so that the Windows values can be hard-coded into the wrapper executable.

15.3.7.3 Cygwin/Windows File Name Conversion

Cygwin provides a Unix emulation environment for Windows. As part of that emulation, it provides a file system mapping that presents the Windows file system in a Unix-compatible manner. Cygwin also provides a utility cygpath that can be used to convert file names and paths between the two representations. In a correctly configured Cygwin installation, cygpath is always present, and is in the PATH.

Libtool uses cygpath to convert from Cygwin (Unix-style) file names and paths to Windows format when the build platform is Cygwin and the host platform is MinGW.

When the host platform is Cygwin, but the build platform is MSYS or some Unix system, libtool also uses cygpath to convert from Windows to Cygwin format (after first converting from the build platform format to Windows format; see see Native MinGW File Name Conversionand see Unix/Windows File Name Conversion). Because the build platform is not Cygwin, cygpath is not (and should not be) in the PATH. Therefore, in this configuration the environment variable LT_CYGPATH is required. See LT_CYGPATH.

15.3.7.4 Unix/Windows File Name Conversion

Wine provides an interpretation environment for some Unix platforms in which Windows applications can be executed. It provides a mapping between the Unix file system and a virtual Windows file system used by the Windows programs. For the file name conversion to work, Wine must be installed and properly configured on the build platform, and the winepath application must be in the build platform'sPATH. In addition, on 32bit GNU/Linux it is usually helpful if the binfmt extension is enabled.

15.3.7.5 LT_CYGPATH

For some cross-compile configurations (where the host platform is Cygwin), the cygpath program is used to convert file names from the build platform notation to the Cygwin form (technically, this conversion is from Windows notation to Cygwin notation; the conversion from the build platform format to Windows notation is performed via other means). However, because the cygpath program is not (and should not be) in the PATH on the build platform, LT_CYGPATH must specify the full build platform file name (that is, the full Unix or MSYS file name) of the cygpath program.

The reason cygpath should not be in the build platform PATH is twofold: first, cygpath is usually installed in the same directory as many other Cygwin executables, such as sed, cp, etc. If the build platform environment had this directory in its PATH, then these Cygwin versions of common Unix utilities might be used in preference to the ones provided by the build platform itself, with deleterious effects. Second, especially when Cygwin-1.7 or later is used, multiple Cygwin installations can coexist within the same Windows instance. Each installation will have separate “mount tables” specified in CYGROOT-N/etc/fstab. These mount tables control how that instance of Cygwin will map Windows file names and paths to Cygwin form. Each installation's cygpath utility automatically deduces the appropriate /etc/fstab file. Since each CYGROOT-N/etc/fstab mount table may specify different mappings, it matters which cygpath is used.

Note that cygpath is a Cygwin application; to execute this tool from Unix requires a working and properly configured Wine installation, as well as enabling the GNU/Linux binfmt extension. Furthermore, the Cygwin setup.exe tool should have been used, via Wine, to properly install Cygwin into the Wine file system (and registry).

Unfortunately, Wine support for Cygwin is intermittent. Recent releases of Cygwin (1.7 and above) appear to require more Windows API support than Wine provides (as of Wine version 1.2); most Cygwin applications fail to execute. This includes cygpath itself. Hence, it is bestnot to use the LT_CYGPATH machinery in libtool when performing Unix to Cygwin cross-compiles. Similarly, it is best not to enable the GNU/Linux binfmt support in this configuration, because while Wine will fail to execute the compiled Cygwin applications, it will still exit with status zero. This tends to confuse build systems and test suites (including libtool's own testsuite, resulting in spurious reported failures). Wine support for the older Cygwin-1.5 series appears satisfactory, but the Cygwin team no longer supports Cygwin-1.5. It is hoped that Wine will eventually be improved such that Cygwin-1.7 will again operate correctly under Wine. Until then, libtool will report warnings as described in see File Name Conversion Failure in these scenarios.

However, LT_CYGPATH is also used for the MSYS to Cygwin cross compile scenario, and operates as expected.

15.3.7.6 Cygwin to MinGW Cross

There are actually three different scenarios that could all legitimately be called a “Cygwin to MinGW” cross compile. The current (and standard) definition is when there is a compiler that produces native Windows libraries and applications, but which itself is a Cygwin application, just as would be expected in any other cross compile setup.

However, historically there were two other definitions, which we will refer to as the fake one, and the lying one.

In the fake Cygwin to MinGW cross compile case, you actually use a native MinGW compiler, but you do so from within a Cygwin environment:

     export PATH="/c/MinGW/bin:${PATH}"
     configure --build=i686-pc-cygwin \
     	--host=mingw32 \
     	NM=/c/MinGW/bin/nm.exe

In this way, the build system “knows” that you are cross compiling, and the file name conversion logic will be used. However, because the tools (mingw32-gcc, nm, ar) used are actually native Windows applications, they will not understand any Cygwin (that is, Unix-like) absolute file names passed as command line arguments (and, unlike MSYS, Cygwin does not automatically convert such arguments). However, so long as only relative file names are used in the build system, and non-Windows-supported Unix idioms such as symlinks and mount points are avoided, this scenario should work.

If you must use absolute file names, you will have to force Libtool to convert file names for the toolchain in this case, by doing the following before you run configure:

     export lt_cv_to_tool_file_cmd=func_convert_file_cygwin_to_w32

In the lying Cygwin to MinGW cross compile case, you lie to the build system:

     export PATH="/c/MinGW/bin:${PATH}"
     configure --build=i686-pc-mingw32 \
     	--host=i686-pc-mingw32 \
     	--disable-dependency-tracking

and claim that the build platform is MinGW, even though you are actually running under Cygwin and not MinGW. In this case, libtool doesnot know that you are performing a cross compile, and thinks instead that you are performing a native MinGW build. However, as described in (see Native MinGW File Name Conversion), that scenario triggers an “MSYS to Windows” file name conversion. This, of course, is the wrong conversion since we are actually running under Cygwin. Also, the toolchain is expecting Windows file names (not Cygwin) but unless told so Libtool will feed Cygwin file names to the toolchain in this case. To force the correct file name conversions in this situation, you should do the following before running configure:

     export lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32
     export lt_cv_to_tool_file_cmd=func_convert_file_cygwin_to_w32

Note that this relies on internal implementation details of libtool, and is subject to change. Also, --disable-dependency-tracking is required, because otherwise the MinGW GCC will generate dependency files that contain Windows file names. This, in turn, will confuse the Cygwinmake program, which does not accept Windows file names:

     Makefile:1: *** target pattern contains no `%'.  Stop.

There have also always been a number of other details required for the lying case to operate correctly, such as the use of so-calledidentity mounts:

     # cygwin-root/etc/fstab
     D:/foo    /foo     some_fs binary 0 0
     D:/bar    /bar     some_fs binary 0 0
     E:/grill  /grill   some_fs binary 0 0

In this way, top-level directories of each drive are available using identical names within Cygwin.

Note that you also need to ensure that the standard Unix directories (like /bin, /lib, /usr, /etc) appear in the root of a drive. This means that you must install Cygwin itself into the C:/ root directory (or D:/, or E:/, etc)—instead of the recommended installation into C:/cygwin/. In addition, all file names used in the build system must be relative, symlinks should not be used within the source or build directory trees, and all -M* options to gcc except -MMD must be avoided.

This is quite a fragile setup, but it has been in historical use, and so is documented here.

15.3.8 Windows DLLs

This topic describes a couple of ways to portably create Windows Dynamic Link Libraries (DLLs). Libtool knows how to create DLLs using GNU tools and using Microsoft tools.

A typical library has a “hidden” implementation with an interface described in a header file. On just about every system, the interface could be something like this:

Example foo.h:

     #ifndef FOO_H
     #define FOO_H
     
     int one (void);
     int two (void);
     extern int three;
     
     #endif /* FOO_H */

And the implementation could be something like this:

Example foo.c:

     #include "foo.h"
     
     int one (void)
     {
       return 1;
     }
     
     int two (void)
     {
       return three - one ();
     }
     
     int three = 3;

When using contemporary GNU tools to create the Windows DLL, the above code will work there too, thanks to its auto-import/auto-export features. But that is not the case when using older GNU tools or perhaps more interestingly when using proprietary tools. In those cases the code will need additional decorations on the interface symbols with __declspec(dllimport) and __declspec(dllexport) depending on whether the library is built or it's consumed and how it's built and consumed. However, it should be noted that it would have worked also with Microsoft tools, if only the variable three hadn't been there, due to the fact the Microsoft tools will automatically import functions (but sadly not variables) and Libtool will automatically export non-static symbols as described next.

With Microsoft tools, Libtool digs through the object files that make up the library, looking for non-static symbols to automatically export. I.e., Libtool with Microsoft tools tries to mimic the auto-export feature of contemporary GNU tools. It should be noted that the GNU auto-export feature is turned off when an explicit __declspec(dllexport) is seen. The GNU tools do this to not make more symbols visible for projects that have already taken the trouble to decorate symbols. There is no similar way to limit which symbols are visible in the code when Libtool is using Microsoft tools. In order to limit symbol visibility in that case you need to use one of the options -export-symbols or -export-symbols-regex.

No matching help with auto-import is provided by Libtool, which is why variables must be decorated to import them from a DLL for everything but contemporary GNU tools. As stated above, functions are automatically imported by both contemporary GNU tools and Microsoft tools, but for other proprietary tools the auto-import status of functions is unknown.

When the objects that form the library are built, there are generally two copies built for each object. One copy is used when linking the DLL and one copy is used for the static library. On Windows systems, a pair of defines are commonly used to discriminate how the interface symbols should be decorated. The first define is ‘-DDLL_EXPORT’ which is automatically provided by Libtool when libtool builds the copy of the object that is destined for the DLL. The second define is ‘-DLIBFOO_BUILD’ (or similar) which is often added by the package providing the library and is used when building the library, but not when consuming the library.

However, the matching double compile is not performed when consuming libraries. It is therefore not possible to reliably distinguish if the consumer is importing from a DLL or if it is going to use a static library.

With contemporary GNU tools, auto-import often saves the day, but see the GNU ld documentation and its --enable-auto-import option for some corner cases when it does not (see --enable-auto-import).

With Microsoft tools you typically get away with always compiling the code such that variables are expected to be imported from a DLL and functions are expected to be found in a static library. The tools will then automatically import the function from a DLL if that is where they are found. If the variables are not imported from a DLL as expected, but are found in a static library that is otherwise pulled in by some function, the linker will issue a warning (LNK4217) that a locally defined symbol is imported, but it still works. In other words, this scheme will not work to only consume variables from a library. There is also a price connected to this liberal use of imports in that an extra indirection is introduced when you are consuming the static version of the library. That extra indirection is unavoidable when the DLL is consumed, but it is not needed when consuming the static library.

For older GNU tools and other proprietary tools there is no generic way to make it possible to consume either of the DLL or the static library without user intervention, the tools need to be told what is intended. One common assumption is that if a DLL is being built (‘DLL_EXPORT’ is defined) then that DLL is going to consume any dependent libraries as DLLs. If that assumption is made everywhere, it is possible to select how an end-user application is consuming libraries by adding a single flag ‘-DDLL_EXPORT’ when a DLL build is required. This is of course an all or nothing deal, either everything as DLLs or everything as static libraries.

To sum up the above, the header file of the foo library needs to be changed into something like this:

Modified foo.h:

     #ifndef FOO_H
     #define FOO_H
     
     #if defined _WIN32 && !defined __GNUC__
     # ifdef LIBFOO_BUILD
     #  ifdef DLL_EXPORT
     #   define LIBFOO_SCOPE            __declspec (dllexport)
     #   define LIBFOO_SCOPE_VAR extern __declspec (dllexport)
     #  endif
     # elif defined _MSC_VER
     #  define LIBFOO_SCOPE
     #  define LIBFOO_SCOPE_VAR  extern __declspec (dllimport)
     # elif defined DLL_EXPORT
     #  define LIBFOO_SCOPE             __declspec (dllimport)
     #  define LIBFOO_SCOPE_VAR  extern __declspec (dllimport)
     # endif
     #endif
     #ifndef LIBFOO_SCOPE
     # define LIBFOO_SCOPE
     # define LIBFOO_SCOPE_VAR extern
     #endif
     
     LIBFOO_SCOPE     int one (void);
     LIBFOO_SCOPE     int two (void);
     LIBFOO_SCOPE_VAR int three;
     
     #endif /* FOO_H */

When the targets are limited to contemporary GNU tools and Microsoft tools, the above can be simplified to the following:

Simplified foo.h:

     #ifndef FOO_H
     #define FOO_H
     
     #if defined _WIN32 && !defined __GNUC__ && !defined LIBFOO_BUILD
     # define LIBFOO_SCOPE_VAR extern __declspec (dllimport)
     #else
     # define LIBFOO_SCOPE_VAR extern
     #endif
     
     int one (void);
     int two (void);
     LIBFOO_SCOPE_VAR int three;
     
     #endif /* FOO_H */

This last simplified version can of course only work when Libtool is used to build the DLL, as no symbols would be exported otherwise (i.e., when using Microsoft tools).

It should be noted that there are various projects that attempt to relax these requirements by various low level tricks, but they are not discussed here. Examples are FlexDLL and edll.

15.4 libtool script contents

Since version 1.4, the libtool script is generated by configure (see Configuring). In earlier versions, configure achieved this by calling a helper script called ltconfig. From libtool version 0.7 to 1.0, this script simply set shell variables, then sourced the libtool backend, ltmain.sh. ltconfigfrom libtool version 1.1 through 1.3 inlined the contents of ltmain.sh into the generated libtool, which improved performance on many systems. The tests that ltconfig used to perform are now kept in libtool.m4 where they can be written using Autoconf. This has the runtime performance benefits of inlined ltmain.sh, and improves the build time a little while considerably easing the amount of raw shell code that used to need maintaining.

The convention used for naming variables that hold shell commands for delayed evaluation, is to use the suffix _cmd where a single line of valid shell script is needed, and the suffix _cmds where multiple lines of shell script may be delayed for later evaluation. By convention,_cmds variables delimit the evaluation units with the ~ character where necessary.

Here is a listing of each of the configuration variables, and how they are used within ltmain.sh (see Configuring):

— Variable: AR

The name of the system library archiver.

— Variable: CC

The name of the compiler used to configure libtool. This will always contain the compiler for the current language (see Tags).

— Variable: ECHO

An echo program that does not interpret backslashes as an escape character. It may be given only one argument, so due quoting is necessary.

— Variable: LD

The name of the linker that libtool should use internally for reloadable linking and possibly shared libraries.

— Variable: LTCC
— Variable: LTCFLAGS

The name of the C compiler and C compiler flags used to configure libtool.

— Variable: NM

The name of a BSD- or MS-compatible program that produces listings of global symbols. For BSD nm, the symbols should be in one the following formats:

          address C global-variable-name
          address D global-variable-name
          address T global-function-name

For MS dumpbin, the symbols should be in one of the following formats:

          counter size    UNDEF    notype       External     | global-var
          counter address section  notype       External     | global-var
          counter address section  notype ()    External     | global-func

The size of the global variables are not zero and the section of the global functions are not "UNDEF". Symbols in "pick any" sections ("pick any" appears in the section header) are not global either.

— Variable: RANLIB

Set to the name of the ranlib program, if any.

— Variable: allow_undefined_flag

The flag that is used by ‘archive_cmds’ in order to declare that there will be unresolved symbols in the resulting shared library. Empty, if no such flag is required. Set to ‘unsupported’ if there is no way to generate a shared library with references to symbols that aren't defined in that library.

— Variable: always_export_symbols

Whether libtool should automatically generate a list of exported symbols using export_symbols_cmds before linking an archive. Set to ‘yes’ or ‘no’. Default is ‘no’.

— Variable: archive_cmds
— Variable: archive_expsym_cmds
— Variable: old_archive_cmds

Commands used to create shared libraries, shared libraries with -export-symbols and static libraries, respectively.

— Variable: archiver_list_spec

Specify filename containing input files for AR.

— Variable: old_archive_from_new_cmds

If the shared library depends on a static library, ‘old_archive_from_new_cmds’ contains the commands used to create that static library. If this variable is not empty, ‘old_archive_cmds’ is not used.

— Variable: old_archive_from_expsyms_cmds

If a static library must be created from the export symbol list in order to correctly link with a shared library, ‘old_archive_from_expsyms_cmds’ contains the commands needed to create that static library. When these commands are executed, the variable soname contains the name of the shared library in question, and the ‘$objdir/$newlib’ contains the path of the static library these commands should build. After executing these commands, libtool will proceed to link against ‘$objdir/$newlib’ instead of soname.

— Variable: lock_old_archive_extraction

Set to ‘yes’ if the extraction of a static library requires locking the library file. This is required on Darwin.

— Variable: build
— Variable: build_alias
— Variable: build_os

Set to the specified and canonical names of the system that libtool was built on.

— Variable: build_libtool_libs

Whether libtool should build shared libraries on this system. Set to ‘yes’ or ‘no’.

— Variable: build_old_libs

Whether libtool should build static libraries on this system. Set to ‘yes’ or ‘no’.

— Variable: compiler_c_o

Whether the compiler supports the -c and -o options simultaneously. Set to ‘yes’ or ‘no’.

— Variable: compiler_needs_object

Whether the compiler has to see an object listed on the command line in order to successfully invoke the linker. If ‘no’, then a set of convenience archives or a set of object file names can be passed via linker-specific options or linker scripts.

— Variable: dlopen_support

Whether dlopen is supported on the platform. Set to ‘yes’ or ‘no’.

— Variable: dlopen_self

Whether it is possible to dlopen the executable itself. Set to ‘yes’ or ‘no’.

— Variable: dlopen_self_static

Whether it is possible to dlopen the executable itself, when it is linked statically (-all-static). Set to ‘yes’ or ‘no’.

— Variable: exclude_expsyms

List of symbols that should not be listed in the preloaded symbols.

— Variable: export_dynamic_flag_spec

Compiler link flag that allows a dlopened shared library to reference symbols that are defined in the program.

— Variable: export_symbols_cmds

Commands to extract exported symbols from libobjs to the file export_symbols.

— Variable: extract_expsyms_cmds

Commands to extract the exported symbols list from a shared library. These commands are executed if there is no file ‘$objdir/$soname-def’, and should write the names of the exported symbols to that file, for the use of ‘old_archive_from_expsyms_cmds’.

— Variable: fast_install

Determines whether libtool will privilege the installer or the developer. The assumption is that installers will seldom run programs in the build tree, and the developer will seldom install. This is only meaningful on platforms whereshlibpath_overrides_runpath is not ‘yes’, so fast_install will be set to ‘needless’ in this case. If fast_install set to ‘yes’, libtool will create programs that search for installed libraries, and, if a program is run in the build tree, a new copy will be linked on-demand to use the yet-to-be-installed libraries. If set to ‘no’, libtool will create programs that use the yet-to-be-installed libraries, and will link a new copy of the program at install time. The default value is ‘yes’ or ‘needless’, depending on platform and configuration flags, and it can be turned from ‘yes’ to ‘no’ with the configure flag --disable-fast-install.

On some systems, the linker always hardcodes paths to dependent libraries into the output. In this case, fast_install is never set to ‘yes’, and relinking at install time is triggered. This also means that DESTDIR installation does not work as expected.

— Variable: file_magic_glob

How to find potential files when deplibs_check_method is ‘file_magic’. file_magic_glob is a sed expression, and the sed instance is fed potential file names that are transformed by the file_magic_glob expression. Useful when the shell does not support the shell option nocaseglob, making want_nocaseglob inappropriate. Normally disabled (i.e. file_magic_glob is empty).

— Variable: finish_cmds

Commands to tell the dynamic linker how to find shared libraries in a specific directory.

— Variable: finish_eval

Same as finish_cmds, except the commands are not displayed.

— Variable: global_symbol_pipe

A pipeline that takes the output of NM, and produces a listing of raw symbols followed by their C names. For example:

          $ eval "$NM progname | $global_symbol_pipe"
          D symbol1 C-symbol1
          T symbol2 C-symbol2
          C symbol3 C-symbol3
          ...
          $

The first column contains the symbol type (used to tell data from code) but its meaning is system dependent.

— Variable: global_symbol_to_cdecl

A pipeline that translates the output of global_symbol_pipe into proper C declarations. Since some platforms, such as HP/UX, have linkers that differentiate code from data, data symbols are declared as data, and code symbols are declared as functions.

— Variable: hardcode_action

Either ‘immediate’ or ‘relink’, depending on whether shared library paths can be hardcoded into executables before they are installed, or if they need to be relinked.

— Variable: hardcode_direct

Set to ‘yes’ or ‘no’, depending on whether the linker hardcodes directories if a library is directly specified on the command line (such as ‘dir/libname.a’) when hardcode_libdir_flag_spec is specified.

— Variable: hardcode_direct_absolute

Some architectures hardcode "absolute" library directories that can not be overridden by shlibpath_var when hardcode_direct is ‘yes’. In that case set hardcode_direct_absolute to ‘yes’, or otherwise ‘no’.

— Variable: hardcode_into_libs

Whether the platform supports hardcoding of run-paths into libraries. If enabled, linking of programs will be much simpler but libraries will need to be relinked during installation. Set to ‘yes’ or ‘no’.

— Variable: hardcode_libdir_flag_spec

Flag to hardcode a libdir variable into a binary, so that the dynamic linker searches libdir for shared libraries at runtime. If it is empty, libtool will try to use some other hardcoding mechanism.

— Variable: hardcode_libdir_separator

If the compiler only accepts a single hardcode_libdir_flag, then this variable contains the string that should separate multiple arguments to that flag.

— Variable: hardcode_minus_L

Set to ‘yes’ or ‘no’, depending on whether the linker hardcodes directories specified by -L flags into the resulting executable when hardcode_libdir_flag_spec is specified.

— Variable: hardcode_shlibpath_var

Set to ‘yes’ or ‘no’, depending on whether the linker hardcodes directories by writing the contents of ‘$shlibpath_var’ into the resulting executable when hardcode_libdir_flag_spec is specified. Set to ‘unsupported’ if directories specified by ‘$shlibpath_var’ are searched at run time, but not at link time.

— Variable: host
— Variable: host_alias
— Variable: host_os

Set to the specified and canonical names of the system that libtool was configured for.

— Variable: include_expsyms

List of symbols that must always be exported when using export_symbols.

— Variable: inherit_rpath

Whether the linker adds runtime paths of dependency libraries to the runtime path list, requiring libtool to relink the output when installing. Set to ‘yes’ or ‘no’. Default is ‘no’.

— Variable: install_override_mode

Permission mode override for installation of shared libraries. If the runtime linker fails to load libraries with wrong permissions, then it may fail to execute programs that are needed during installation, because these need the library that has just been installed. In this case, it is necessary to pass the mode to install with -m install_override_mode.

— Variable: libext

The standard old archive suffix (normally ‘a’).

— Variable: libname_spec

The format of a library name prefix. On all Unix systems, static libraries are called ‘libname.a’, but on some systems (such as OS/2 or MS-DOS), the library is just called ‘name.a’.

— Variable: library_names_spec

A list of shared library names. The first is the name of the file, the rest are symbolic links to the file. The name in the list is the file name that the linker finds when given -lname.

— Variable: link_all_deplibs

Whether libtool must link a program against all its dependency libraries. Set to ‘yes’ or ‘no’. Default is ‘unknown’, which is a synonym for ‘yes’.

— Variable: link_static_flag

Linker flag (passed through the C compiler) used to prevent dynamic linking.

— Variable: macro_version
— Variable: macro_revision

The release and revision from which the libtool.m4 macros were taken. This is used to ensure that macros and ltmain.shcorrespond to the same Libtool version.

— Variable: max_cmd_len

The approximate longest command line that can be passed to ‘$SHELL’ without being truncated, as computed by ‘LT_CMD_MAX_LEN’.

— Variable: need_lib_prefix

Whether we can dlopen modules without a ‘lib’ prefix. Set to ‘yes’ or ‘no’. By default, it is ‘unknown’, which means the same as ‘yes’, but documents that we are not really sure about it. ‘no’ means that it is possible to dlopen a module without the ‘lib’ prefix.

— Variable: need_version

Whether versioning is required for libraries, i.e. whether the dynamic linker requires a version suffix for all libraries. Set to ‘yes’ or ‘no’. By default, it is ‘unknown’, which means the same as ‘yes’, but documents that we are not really sure about it.

— Variable: need_locks

Whether files must be locked to prevent conflicts when compiling simultaneously. Set to ‘yes’ or ‘no’.

— Variable: nm_file_list_spec

Specify filename containing input files for NM.

— Variable: no_builtin_flag

Compiler flag to disable builtin functions that conflict with declaring external global symbols as char.

— Variable: no_undefined_flag

The flag that is used by ‘archive_cmds’ in order to declare that there will be no unresolved symbols in the resulting shared library. Empty, if no such flag is required.

— Variable: objdir

The name of the directory that contains temporary libtool files.

— Variable: objext

The standard object file suffix (normally ‘o’).

— Variable: pic_flag

Any additional compiler flags for building library object files.

— Variable: postinstall_cmds
— Variable: old_postinstall_cmds

Commands run after installing a shared or static library, respectively.

— Variable: postuninstall_cmds
— Variable: old_postuninstall_cmds

Commands run after uninstalling a shared or static library, respectively.

— Variable: postlink_cmds

Commands necessary for finishing linking programs. postlink_cmds are executed immediately after the program is linked. Any occurrence of the string @OUTPUT@ in postlink_cmds is replaced by the name of the created executable (i.e. not the wrapper, if a wrapper is generated) prior to execution. Similarly, @TOOL_OUTPUT@ is replaced by the toolchain format of @OUTPUT@. Normally disabled (i.e. postlink_cmds empty).

— Variable: reload_cmds
— Variable: reload_flag

Commands to create a reloadable object. Set reload_cmds to ‘false’ on systems that cannot create reloadable objects.

— Variable: runpath_var

The environment variable that tells the linker which directories to hardcode in the resulting executable.

— Variable: shlibpath_overrides_runpath

Indicates whether it is possible to override the hard-coded library search path of a program with an environment variable. If this is set to no, libtool may have to create two copies of a program in the build tree, one to be installed and one to be run in the build tree only. When each of these copies is created depends on the value of fast_install. The default value is ‘unknown’, which is equivalent to ‘no’.

— Variable: shlibpath_var

The environment variable that tells the dynamic linker where to find shared libraries.

— Variable: soname_spec

The name coded into shared libraries, if different from the real name of the file.

— Variable: striplib
— Variable: old_striplib

Command to strip a shared (striplib) or static (old_striplib) library, respectively. If these variables are empty, the strip flag in the install mode will be ignored for libraries (see Install mode).

— Variable: sys_lib_dlsearch_path_spec

Expression to get the run-time system library search path. Directories that appear in this list are never hard-coded into executables.

— Variable: sys_lib_search_path_spec

Expression to get the compile-time system library search path. This variable is used by libtool when it has to test whether a certain library is shared or static. The directories listed in shlibpath_var are automatically appended to this list, every time libtool runs (i.e., not at configuration time), because some linkers use this variable to extend the library search path. Linker switches such as -L also augment the search path.

— Variable: thread_safe_flag_spec

Linker flag (passed through the C compiler) used to generate thread-safe libraries.

— Variable: to_host_file_cmd

If the toolchain is not native to the build platform (e.g. if you are using MSYS to drive the scripting, but are using the MinGW native Windows compiler) this variable describes how to convert file names from the format used by the build platform to the format used by host platform. Normally set to ‘func_convert_file_noop’, libtool will autodetect most cases in which other values should be used. On rare occasions, it may be necessary to override the autodetected value (see Cygwin to MinGW Cross).

— Variable: to_tool_file_cmd

If the toolchain is not native to the build platform (e.g. if you are using some Unix to drive the scripting together with a Windows toolchain running in Wine) this variable describes how to convert file names from the format used by the build platform to the format used by the toolchain. Normally set to ‘func_convert_file_noop’.

— Variable: version_type

The library version numbering type. One of ‘libtool’, ‘freebsd-aout’, ‘freebsd-elf’, ‘irix’, ‘linux’, ‘osf’, ‘sunos’, ‘windows’, or ‘none’.

— Variable: want_nocaseglob

Find potential files using the shell option nocaseglob, when deplibs_check_method is ‘file_magic’. Normally set to ‘no’. Set to ‘yes’ to enable the nocaseglob shell option when looking for potential file names in a case-insensitive manner.

— Variable: whole_archive_flag_spec

Compiler flag to generate shared objects from convenience archives.

— Variable: wl

The C compiler flag that allows libtool to pass a flag directly to the linker. Used as: ${wl}some-flag.

Variables ending in ‘_cmds’ or ‘_eval’ contain a ‘~’-separated list of commands that are evaled one after another. If any of the commands return a nonzero exit status, libtool generally exits with an error message.

Variables ending in ‘_spec’ are evaled before being used by libtool.

15.5 Cheap tricks

Here are a few tricks that you can use in order to make maintainership easier:

  • When people report bugs, ask them to use the --config, --debug, or --features flags, if you think they will help you. These flags are there to help you get information directly, rather than having to trust second-hand observation.
  • Rather than reconfiguring libtool every time I make a change to ltmain.in, I keep a permanent libtool script in my PATH, which sourcesltmain.in directly.

    The following steps describe how to create such a script, where /home/src/libtool is the directory containing the libtool source tree,/home/src/libtool/libtool is a libtool script that has been configured for your platform, and ~/bin is a directory in your PATH:

              trick$ cd ~/bin
              trick$ sed 's%^\(macro_version=\).*$%\1@VERSION@%;
                          s%^\(macro_revision=\).*$%\1@package_revision@%;
                          /^# ltmain\.sh/q' /home/src/libtool/libtool > libtool
              trick$ echo '. /home/src/libtool/ltmain.in' >> libtool
              trick$ chmod +x libtool
              trick$ libtool --version
              ltmain.sh (GNU @PACKAGE@@TIMESTAMP@) @VERSION@
              
              Copyright (C) 2011 Free Software Foundation, Inc.
              This is free software; see the source for copying conditions.  There is NO
              warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
              trick$
    

The output of the final ‘libtool --version’ command shows that the ltmain.in script is being used directly. Now, modify ~/bin/libtool or/home/src/libtool/ltmain.in directly in order to test new changes without having to rerun configure.


Next: , Previous: Maintaining, Up: Top

Appendix A GNU Free Documentation License

Version 1.3, 3 November 2008
     Copyright © 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc.
     http://fsf.org/
     
     Everyone is permitted to copy and distribute verbatim copies
     of this license document, but changing it is not allowed.
  1. PREAMBLE

    The purpose of this License is to make a manual, textbook, or other functional and useful document free in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others.

    This License is a kind of “copyleft”, which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software.

    We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference.

  2. APPLICABILITY AND DEFINITIONS

    This License applies to any manual or other work, in any medium, that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. Such a notice grants a world-wide, royalty-free license, unlimited in duration, to use that work under the conditions stated herein. The “Document”, below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as “you”. You accept the license if you copy, modify or distribute the work in a way requiring permission under copyright law.

    A “Modified Version” of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language.

    A “Secondary Section” is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document's overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (Thus, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them.

    The “Invariant Sections” are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License. If a section does not fit the above definition of Secondary then it is not allowed to be designated as Invariant. The Document may contain zero Invariant Sections. If the Document does not identify any Invariant Sections then there are none.

    The “Cover Texts” are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License. A Front-Cover Text may be at most 5 words, and a Back-Cover Text may be at most 25 words.

    A “Transparent” copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, that is suitable for revising the document straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup, or absence of markup, has been arranged to thwart or discourage subsequent modification by readers is not Transparent. An image format is not Transparent if used for any substantial amount of text. A copy that is not “Transparent” is called “Opaque”.

    Examples of suitable formats for Transparent copies include plain ascii without markup, Texinfo input format, LaTeX input format,SGML or XML using a publicly available DTD, and standard-conforming simple HTML, PostScript or PDF designed for human modification. Examples of transparent image formats include PNG, XCF and JPG. Opaque formats include proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generated HTML, PostScript or PDF produced by some word processors for output purposes only.

    The “Title Page” means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, “Title Page” means the text near the most prominent appearance of the work's title, preceding the beginning of the body of the text.

    The “publisher” means any person or entity that distributes copies of the Document to the public.

    A section “Entitled XYZ” means a named subunit of the Document whose title either is precisely XYZ or contains XYZ in parentheses following text that translates XYZ in another language. (Here XYZ stands for a specific section name mentioned below, such as “Acknowledgements”, “Dedications”, “Endorsements”, or “History”.) To “Preserve the Title” of such a section when you modify the Document means that it remains a section “Entitled XYZ” according to this definition.

    The Document may include Warranty Disclaimers next to the notice which states that this License applies to the Document. These Warranty Disclaimers are considered to be included by reference in this License, but only as regards disclaiming warranties: any other implication that these Warranty Disclaimers may have is void and has no effect on the meaning of this License.

  3. VERBATIM COPYING

    You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3.

    You may also lend copies, under the same conditions stated above, and you may publicly display copies.

  4. COPYING IN QUANTITY

    If you publish printed copies (or copies in media that commonly have printed covers) of the Document, numbering more than 100, and the Document's license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects.

    If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages.

    If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a computer-network location from which the general network-using public has access to download using public-standard network protocols a complete Transparent copy of the Document, free of added material. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public.

    It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document.

  5. MODIFICATIONS

    You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version:

    1. Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission.
    2. List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has fewer than five), unless they release you from this requirement.
    3. State on the Title page the name of the publisher of the Modified Version, as the publisher.
    4. Preserve all the copyright notices of the Document.
    5. Add an appropriate copyright notice for your modifications adjacent to the other copyright notices.
    6. Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below.
    7. Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document's license notice.
    8. Include an unaltered copy of this License.
    9. Preserve the section Entitled “History”, Preserve its Title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section Entitled “History” in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence.
    10. Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the “History” section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission.
    11. For any section Entitled “Acknowledgements” or “Dedications”, Preserve the Title of the section, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein.
    12. Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles.
    13. Delete any section Entitled “Endorsements”. Such a section may not be included in the Modified Version.
    14. Do not retitle any existing section to be Entitled “Endorsements” or to conflict in title with any Invariant Section.
    15. Preserve any Warranty Disclaimers.

    If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. These titles must be distinct from any other section titles.

    You may add a section Entitled “Endorsements”, provided it contains nothing but endorsements of your Modified Version by various parties—for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard.

    You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one.

    The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version.

  6. COMBINING DOCUMENTS

    You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice, and that you preserve all their Warranty Disclaimers.

    The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work.

    In the combination, you must combine any sections Entitled “History” in the various original documents, forming one section Entitled “History”; likewise combine any sections Entitled “Acknowledgements”, and any sections Entitled “Dedications”. You must delete all sections Entitled “Endorsements.”

  7. COLLECTIONS OF DOCUMENTS

    You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects.

    You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document.

  8. AGGREGATION WITH INDEPENDENT WORKS

    A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, is called an “aggregate” if the copyright resulting from the compilation is not used to limit the legal rights of the compilation's users beyond what the individual works permit. When the Document is included in an aggregate, this License does not apply to the other works in the aggregate which are not themselves derivative works of the Document.

    If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one half of the entire aggregate, the Document's Cover Texts may be placed on covers that bracket the Document within the aggregate, or the electronic equivalent of covers if the Document is in electronic form. Otherwise they must appear on printed covers that bracket the whole aggregate.

  9. TRANSLATION

    Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License, and all the license notices in the Document, and any Warranty Disclaimers, provided that you also include the original English version of this License and the original versions of those notices and disclaimers. In case of a disagreement between the translation and the original version of this License or a notice or disclaimer, the original version will prevail.

    If a section in the Document is Entitled “Acknowledgements”, “Dedications”, or “History”, the requirement (section 4) to Preserve its Title (section 1) will typically require changing the actual title.

  10. TERMINATION

    You may not copy, modify, sublicense, or distribute the Document except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, or distribute it is void, and will automatically terminate your rights under this License.

    However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.

    Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.

    Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, receipt of a copy of some or all of the same material does not give you any rights to use it.

  11. FUTURE REVISIONS OF THIS LICENSE

    The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Seehttp://www.gnu.org/copyleft/.

    Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License “or any later version” applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation. If the Document specifies that a proxy can decide which future versions of this License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Document.

  12. RELICENSING

    “Massive Multiauthor Collaboration Site” (or “MMC Site”) means any World Wide Web server that publishes copyrightable works and also provides prominent facilities for anybody to edit those works. A public wiki that anybody can edit is an example of such a server. A “Massive Multiauthor Collaboration” (or “MMC”) contained in the site means any set of copyrightable works thus published on the MMC site.

    “CC-BY-SA” means the Creative Commons Attribution-Share Alike 3.0 license published by Creative Commons Corporation, a not-for-profit corporation with a principal place of business in San Francisco, California, as well as future copyleft versions of that license published by that same organization.

    “Incorporate” means to publish or republish a Document, in whole or in part, as part of another Document.

    An MMC is “eligible for relicensing” if it is licensed under this License, and if all works that were first published under this License somewhere other than this MMC, and subsequently incorporated in whole or in part into the MMC, (1) had no cover texts or invariant sections, and (2) were thus incorporated prior to November 1, 2008.

    The operator of an MMC Site may republish an MMC contained in the site under CC-BY-SA on the same site at any time before August 1, 2009, provided the MMC is eligible for relicensing.

ADDENDUM: How to use this License for your documents

To use this License in a document you have written, include a copy of the License in the document and put the following copyright and license notices just after the title page:

       Copyright (C)  year  your name.
       Permission is granted to copy, distribute and/or modify this document
       under the terms of the GNU Free Documentation License, Version 1.3
       or any later version published by the Free Software Foundation;
       with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
       Texts.  A copy of the license is included in the section entitled ``GNU
       Free Documentation License''.

If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, replace the “with...Texts.” line with this:

         with the Invariant Sections being list their titles, with
         the Front-Cover Texts being list, and with the Back-Cover Texts
         being list.

If you have Invariant Sections without Cover Texts, or some other combination of the three, merge those two alternatives to suit the situation.

If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software.

Combined Index


Footnotes

[1] If you don't specify an rpath, then libtool builds a libtool convenience archive, not a shared library (see Static libraries).

[2] However, you should avoid using -L or -l flags to link against an uninstalled libtool library. Just specify the relative path to the .la file, such as ../intl/libintl.la. This is a design decision to eliminate any ambiguity when linking against uninstalled shared libraries.

[3] And why should we? main.o doesn't directly depend on -lm after all.

[4] Don't strip static libraries though, or they will be unusable.

[5] Since GNU Automake 1.5, the flags -dlopen or -dlpreopen (see Link mode) can be employed with the ‘program_LDADD’ variable. Unfortunately, older releases didn't accept these flags, so if you are stuck with an ancient Automake, we recommend quoting the flag itself, and setting ‘program_DEPENDENCIES’ too:

     program_LDADD = "-dlopen" libfoo.la
     program_DEPENDENCIES = libfoo.la

[6] LT_INIT requires that you define the Makefile variable top_builddir in your Makefile.in. Automake does this automatically, but Autoconf users should set it to the relative path to the top of your build directory (../.., for example).

[7] GNU Image Manipulation Program, for those who haven't taken the plunge. See http://www.gimp.org/.

[8] We used to recommend __P, __BEGIN_DECLS and __END_DECLS. This was bad advice since symbols (even preprocessor macro names) that begin with an underscore are reserved for the use of the compiler.

[9] LIBPATH on AIX, and SHLIB_PATH on HP-UX.

[10] Some platforms, notably Mac OS X, differentiate between a runtime library that cannot be opened by lt_dlopen and a dynamic module that can. For maximum portability you should try to ensure that you only pass lt_dlopen objects that have been compiled with libtool's -module flag.

[11] This is used for the host dependent module loading API – shl_load and LoadLibrary for example

[12] We used to recommend adding the contents of ltdl.m4 to acinclude.m4, but with aclocal from a modern Automake (1.8 or newer) and this release of libltdl that is not only unnecessary but makes it easy to forget to upgrade acinclude.m4 if you move to a different release of libltdl.

[13] Even if libltdl is installed, ‘LTDL_INIT’ may fail to detect it if libltdl depends on symbols provided by libraries other than the C library.

[14] All code compiled for the PowerPC and RS/6000 chips (powerpc-*-*, powerpcle-*-*, and rs6000-*-*) is position-independent, regardless of the operating system or compiler suite. So, “regular objects” can be used to build shared libraries on these systems and no special PIC compiler flags are required.

크리에이티브 커먼즈 라이센스
Creative Commons License
2012/04/17 13:49 2012/04/17 13:49
받은 트랙백이 없고, 댓글이 없습니다.

댓글+트랙백 RSS :: http://blog.hwport.com/rss/response/654

댓글+트랙백 ATOM :: http://blog.hwport.com/atom/response/654



본 내용은 "http://forums-web2.gentoo.org/viewtopic-t-835256-start-0.html" 에서 발췌한 것임을 밝힙니다.

이 내용은 openssl의 심볼에 내부적으로 버젼을 붙여주는 방법을 어떻게 이룰수 있는지에 대한 힌트가 담겨져 있습니다.

version script 를 어떻게 설정하여 사용하는지에 대한 유용한 정보라고 생각됩니다.

대략적으로 아래와 같은 메세지로 인하여 골치를 썩고 계신분들께 유용한 정보라고 할수 있겠죠.

undefined reference to `EVP_enc_null@OPENSSL_0.9.8'
undefined reference to `EVP_CIPHER_CTX_init@OPENSSL_0.9.8'
undefined reference to `EVP_DecodeInit@OPENSSL_0.9.8'
undefined reference to `EVP_DecryptUpdate@OPENSSL_0.9.8'
undefined reference to `EVP_DecryptFinal_ex@OPENSSL_0.9.8'
undefined reference to `EVP_CIPHER_CTX_cleanup@OPENSSL_0.9.8'
undefined reference to `EVP_DecodeFinal@OPENSSL_0.9.8'
undefined reference to `EVP_DecryptInit_ex@OPENSSL_0.9.8'
undefined reference to `EVP_DecodeUpdate@OPENSSL_0.9.8'
undefined reference to `EVP_CIPHER_CTX_set_padding@OPENSSL_0.9.8'
undefined reference to `EVP_aes_128_cbc@OPENSSL_0.9.8'



----


Hi,
I've installed git a few weeks ago and never had an issue. Today I was surprised by two error messages (git commands continued to work despite the messages, but gitk refused to start):

Code:
git: /lib/libz.so.1.2.3: no version information available (required by git)
git: /usr/lib/libcrypto.so.0.9.8: no version information available (required by git)


(I'm not 100% sure about the libz version above, I've reconstructed that message from brain memory.)

As I did not understand what change I made to break git, I could not attack the problem at its specific cause. I found solutions that are not ugly hacks though.

I got rid of the first error by installing libz 1.2.5. The second one was more sophisticated. I found http://chris.dzombak.name/blog/2010/03/building-openssl-with-symbol-versioning/ and was able to create an overlay for openssl-0.9.8o that got rid of the libcrypto error.

Code:
--- /usr/portage/dev-libs/openssl/openssl-0.9.8o.ebuild   2010-06-21 23:05:41.000000000 +0200
+++ /usr/local/portage/dev-libs/openssl/openssl-0.9.8o.ebuild   2010-07-08 22:36:42.000000000 +0200
@@ -30,6 +30,7 @@
    epatch "${FILESDIR}"/${PN}-0.9.8e-bsd-sparc64.patch
    epatch "${FILESDIR}"/${PN}-0.9.8h-ldflags.patch #181438
    epatch "${FILESDIR}"/${PN}-0.9.8m-binutils.patch #289130
+   epatch "${FILESDIR}"/${PN}-0.9.8l-sym-version-felixrabe.patch
 
    # disable fips in the build
    # make sure the man pages are suffixed #302165


The file /usr/local/portage/dev-libs/openssl/files/openssl-0.9.8l-sym-version-felixrabe.patch is identical to the patch given at the website above:

Code:
--- openssl-0.9.8l.orig/Configure   2009-11-05 07:07:06.000000000 -0500
+++ openssl-0.9.8l/Configure   2010-03-14 18:18:14.000000000 -0400
@@ -1434,6 +1434,8 @@
    $shlib_minor=$2;
    }
 
+$shared_ldflag .= " -Wl,--version-script=openssl.ld";
+
 open(IN,'<Makefile.org') || die "unable to read Makefile.org:$!\n";
 unlink("$Makefile.new") || die "unable to remove old $Makefile.new:$!\n" if -e "$Makefile.new";
 open(OUT,">$Makefile.new") || die "unable to create $Makefile.new:$!\n";
--- openssl-0.9.8l.orig/Makefile   2009-11-05 11:15:53.000000000 -0500
+++ openssl-0.9.8l/Makefile   2010-03-14 18:19:12.000000000 -0400
@@ -168,9 +168,9 @@
 SHARED_CRYPTO=libcrypto$(SHLIB_EXT)
 SHARED_SSL=libssl$(SHLIB_EXT)
 SHARED_FIPS=
-SHARED_LIBS=
-SHARED_LIBS_LINK_EXTS=
-SHARED_LDFLAGS=
+SHARED_LIBS=$(SHARED_FIPS) $(SHARED_CRYPTO) $(SHARED_SSL)
+SHARED_LIBS_LINK_EXTS=.so.$(SHLIB_MAJOR) .so
+SHARED_LDFLAGS=-m64 -Wl,--version-script=openssl.ld
 
 GENERAL=        Makefile
 BASENAME=       openssl
--- openssl-0.9.8l.orig/engines/openssl.ld   1969-12-31 19:00:00.000000000 -0500
+++ openssl-0.9.8l/engines/openssl.ld   2010-03-14 18:18:55.000000000 -0400
@@ -0,0 +1,4 @@
+OPENSSL_0.9.8 {
+    global:
+       *;
+};
--- openssl-0.9.8l.orig/openssl.ld   1969-12-31 19:00:00.000000000 -0500
+++ openssl-0.9.8l/openssl.ld   2010-03-14 18:18:15.000000000 -0400
@@ -0,0 +1,4 @@
+OPENSSL_0.9.8 {
+    global:
+       *;
+};


If possible, please include this patch in Gentoo for dev-libs/openssl 0.9.8o.

Greetings,
Felix
 
크리에이티브 커먼즈 라이센스
Creative Commons License
2012/04/10 23:28 2012/04/10 23:28
받은 트랙백이 없고, 댓글이 없습니다.

댓글+트랙백 RSS :: http://blog.hwport.com/rss/response/653

댓글+트랙백 ATOM :: http://blog.hwport.com/atom/response/653


본 내용은 "http://biot.com/capstats/bpf.html" 에서 발췌한 것임을 밝힙니다.

----


Berkeley Packet Filter (BPF) syntax

The expression consists of one or more primitives. Primitives usually consist of an id (name or number) preceded by one or more qualifiers. There are three different kinds of qualifier:
type
qualifiers say what kind of thing the id name or number refers to. Possible types are host, net , port and portrange. E.g., `host foo', `net 128.3', `port 20', `portrange 6000-6008'. If there is no type qualifier, host is assumed.
dir
qualifiers specify a particular transfer direction to and/or from id. Possible directions are src, dst, src or dst and src and dst. E.g., `src foo', `dst net 128.3', `src or dst port ftp-data'. If there is no dir qualifier, src or dst is assumed. For some link layers, such as SLIP and the ``cooked'' Linux capture mode used for the ``any'' device and for some other device types, the inbound and outbound qualifiers can be used to specify a desired direction.
proto
qualifiers restrict the match to a particular protocol. Possible protos are: ether, fddi, tr, wlan, ip, ip6, arp, rarp, decnet, tcp and udp. E.g., `ether src foo', `arp net 128.3', `tcp port 21', `udp portrange 7000-7009'. If there is no proto qualifier, all protocols consistent with the type are assumed. E.g., `src foo' means `(ip or arp or rarp) src foo' (except the latter is not legal syntax), `net bar' means `(ip or arp or rarp) net bar' and `port 53' means `(tcp or udp) port 53'.

`fddi' is actually an alias for `ether'; the parser treats them identically as meaning ``the data link level used on the specified network interface.'' FDDI headers contain Ethernet-like source and destination addresses, and often contain Ethernet-like packet types, so you can filter on these FDDI fields just as with the analogous Ethernet fields. FDDI headers also contain other fields, but you cannot name them explicitly in a filter expression.

Similarly, `tr' and `wlan' are aliases for `ether'; the previous paragraph's statements about FDDI headers also apply to Token Ring and 802.11 wireless LAN headers. For 802.11 headers, the destination address is the DA field and the source address is the SA field; the BSSID, RA, and TA fields aren't tested.

In addition to the above, there are some special `primitive' keywords that don't follow the pattern: gateway, broadcast, less, greater and arithmetic expressions. All of these are described below.

More complex filter expressions are built up by using the words and, or and not to combine primitives. E.g., `host foo and not port ftp and not port ftp-data'. To save typing, identical qualifier lists can be omitted. E.g., `tcp dst port ftp or ftp-data or domain' is exactly the same as `tcp dst port ftp or tcp dst port ftp-data or tcp dst port domain'.

Allowable primitives are:

dst host host
True if the IPv4/v6 destination field of the packet is host, which may be either an address or a name.
src host host
True if the IPv4/v6 source field of the packet is host.
host host
True if either the IPv4/v6 source or destination of the packet is host.
Any of the above host expressions can be prepended with the keywords, ip, arp, rarp, or ip6 as in:
ip host host
which is equivalent to:
ether proto \ip and host host
If host is a name with multiple IP addresses, each address will be checked for a match.
ether dst ehost
True if the Ethernet destination address is ehost. Ehost may be either a name from /etc/ethers or a number (see ethers(5) for numeric format).
ether src ehost
True if the Ethernet source address is ehost.
ether host ehost
True if either the Ethernet source or destination address is ehost.
gateway host
True if the packet used host as a gateway. I.e., the Ethernet source or destination address was host but neither the IP source nor the IP destination was host. Host must be a name and must be found both by the machine's host-name-to-IP-address resolution mechanisms (host name file, DNS, NIS, etc.) and by the machine's host-name-to-Ethernet-address resolution mechanism (/etc/ethers, etc.). (An equivalent expression is
ether host ehost and not host host
which can be used with either names or numbers for host / ehost.) This syntax does not work in IPv6-enabled configuration at this moment.
dst net net
True if the IPv4/v6 destination address of the packet has a network number of net. Net may be either a name from the networks database (/etc/networks, etc.) or a network number. An IPv4 network number can be written as a dotted quad (e.g., 192.168.1.0), dotted triple (e.g., 192.168.1), dotted pair (e.g, 172.16), or single number (e.g., 10); the netmask is 255.255.255.255 for a dotted quad (which means that it's really a host match), 255.255.255.0 for a dotted triple, 255.255.0.0 for a dotted pair, or 255.0.0.0 for a single number. An IPv6 network number must be written out fully; the netmask is ff:ff:ff:ff:ff:ff:ff:ff, so IPv6 "network" matches are really always host matches, and a network match requires a netmask length.
src net net
True if the IPv4/v6 source address of the packet has a network number of net.
net net
True if either the IPv4/v6 source or destination address of the packet has a network number of net.
net net mask netmask
True if the IPv4 address matches net with the specific netmask. May be qualified with src or dst. Note that this syntax is not valid for IPv6 net.
net net/len
True if the IPv4/v6 address matches net with a netmask len bits wide. May be qualified with src or dst.
dst port port
True if the packet is ip/tcp, ip/udp, ip6/tcp or ip6/udp and has a destination port value of port. The port can be a number or a name used in /etc/services (see tcp(7) and udp(7)). If a name is used, both the port number and protocol are checked. If a number or ambiguous name is used, only the port number is checked (e.g., dst port 513 will print both tcp/login traffic and udp/who traffic, and port domain will print both tcp/domain and udp/domain traffic).
src port port
True if the packet has a source port value of port.
port port
True if either the source or destination port of the packet is port.
dst portrange port1-port2
True if the packet is ip/tcp, ip/udp, ip6/tcp or ip6/udp and has a destination port value between port1 and port2. port1 and port2 are interpreted in the same fashion as the port parameter for port.
src portrange port1-port2
True if the packet has a source port value between port1 and port2.
portrange port1-port2
True if either the source or destination port of the packet is between port1 and port2.
Any of the above port or port range expressions can be prepended with the keywords, tcp or udp, as in:
tcp src port port
which matches only tcp packets whose source port is port.
less length
True if the packet has a length less than or equal to length. This is equivalent to:
len <= length.
greater length
True if the packet has a length greater than or equal to length. This is equivalent to:
len >= length.
ip proto protocol
True if the packet is an IPv4 packet (see ip(4P)) of protocol type protocol. Protocol can be a number or one of the names icmp, icmp6, igmp, igrp, pim, ah, esp, vrrp, udp, or tcp. Note that the identifiers tcp, udp, and icmp are also keywords and must be escaped via backslash (\), which is \\ in the C-shell. Note that this primitive does not chase the protocol header chain.
ip6 proto protocol
True if the packet is an IPv6 packet of protocol type protocol. Note that this primitive does not chase the protocol header chain.
ip6 protochain protocol
True if the packet is IPv6 packet, and contains protocol header with type protocol in its protocol header chain. For example,
ip6 protochain 6
matches any IPv6 packet with TCP protocol header in the protocol header chain. The packet may contain, for example, authentication header, routing header, or hop-by-hop option header, between IPv6 header and TCP header. The BPF code emitted by this primitive is complex and cannot be optimized by BPF optimizer code in tcpdump, so this can be somewhat slow.
ip protochain protocol
Equivalent to ip6 protochain protocol, but this is for IPv4.
ether broadcast
True if the packet is an Ethernet broadcast packet. The ether keyword is optional.
ip broadcast
True if the packet is an IPv4 broadcast packet. It checks for both the all-zeroes and all-ones broadcast conventions, and looks up the subnet mask on the interface on which the capture is being done.
If the subnet mask of the interface on which the capture is being done is not available, either because the interface on which capture is being done has no netmask or because the capture is being done on the Linux "any" interface, which can capture on more than one interface, this check will not work correctly.
ether multicast
True if the packet is an Ethernet multicast packet. The ether keyword is optional. This is shorthand for `ether[0] & 1 != 0'.
ip multicast
True if the packet is an IPv4 multicast packet.
ip6 multicast
True if the packet is an IPv6 multicast packet.
ether proto protocol
True if the packet is of ether type protocol. Protocol can be a number or one of the names ip, ip6, arp, rarp, atalk, aarp, decnet, sca, lat, mopdl, moprc, iso, stp, ipx, or netbeui. Note these identifiers are also keywords and must be escaped via backslash (\).
[In the case of FDDI (e.g., `fddi protocol arp'), Token Ring (e.g., `tr protocol arp'), and IEEE 802.11 wireless LANS (e.g., `wlan protocol arp'), for most of those protocols, the protocol identification comes from the 802.2 Logical Link Control (LLC) header, which is usually layered on top of the FDDI, Token Ring, or 802.11 header.
When filtering for most protocol identifiers on FDDI, Token Ring, or 802.11, tcpdump checks only the protocol ID field of an LLC header in so-called SNAP format with an Organizational Unit Identifier (OUI) of 0x000000, for encapsulated Ethernet; it doesn't check whether the packet is in SNAP format with an OUI of 0x000000. The exceptions are:
iso
tcpdump checks the DSAP (Destination Service Access Point) and SSAP (Source Service Access Point) fields of the LLC header;
stp and netbeui
tcpdump checks the DSAP of the LLC header;
atalk
tcpdump checks for a SNAP-format packet with an OUI of 0x080007 and the AppleTalk etype.
In the case of Ethernet, tcpdump checks the Ethernet type field for most of those protocols. The exceptions are:
iso, stp, and netbeui
tcpdump checks for an 802.3 frame and then checks the LLC header as it does for FDDI, Token Ring, and 802.11;
atalk
tcpdump checks both for the AppleTalk etype in an Ethernet frame and for a SNAP-format packet as it does for FDDI, Token Ring, and 802.11;
aarp
tcpdump checks for the AppleTalk ARP etype in either an Ethernet frame or an 802.2 SNAP frame with an OUI of 0x000000;
ipx
tcpdump checks for the IPX etype in an Ethernet frame, the IPX DSAP in the LLC header, the 802.3-with-no-LLC-header encapsulation of IPX, and the IPX etype in a SNAP frame.
decnet src host
True if the DECNET source address is host, which may be an address of the form ``10.123'', or a DECNET host name. [DECNET host name support is only available on ULTRIX systems that are configured to run DECNET.]
decnet dst host
True if the DECNET destination address is host.
decnet host host
True if either the DECNET source or destination address is host.
ifname interface
True if the packet was logged as coming from the specified interface (applies only to packets logged by OpenBSD's pf(4)).
on interface
Synonymous with the ifname modifier.
rnr num
True if the packet was logged as matching the specified PF rule number (applies only to packets logged by OpenBSD's pf(4)).
rulenum num
Synonymous with the rnr modifier.
reason code
True if the packet was logged with the specified PF reason code. The known codes are: match, bad-offset, fragment, short, normalize, and memory (applies only to packets logged by OpenBSD's pf(4)).
rset name
True if the packet was logged as matching the specified PF ruleset name of an anchored ruleset (applies only to packets logged by pf(4)).
ruleset name
Synonymous with the rset modifier.
srnr num
True if the packet was logged as matching the specified PF rule number of an anchored ruleset (applies only to packets logged by pf(4)).
subrulenum num
Synonymous with the srnr modifier.
action act
True if PF took the specified action when the packet was logged. Known actions are: pass and block (applies only to packets logged by OpenBSD's pf(4)).
ip, ip6, arp, rarp, atalk, aarp, decnet, iso, stp, ipx, netbeui
Abbreviations for:
ether proto p
where p is one of the above protocols.
lat, moprc, mopdl
Abbreviations for:
ether proto p
where p is one of the above protocols. Note that tcpdump does not currently know how to parse these protocols.
vlan [vlan_id]
True if the packet is an IEEE 802.1Q VLAN packet. If [vlan_id] is specified, only true if the packet has the specified vlan_id. Note that the first vlan keyword encountered in expression changes the decoding offsets for the remainder of expression on the assumption that the packet is a VLAN packet. The vlan [vlan_id] expression may be used more than once, to filter on VLAN hierarchies. Each use of that expression increments the filter offsets by 4.
For example:
vlan 100 && vlan 200
filters on VLAN 200 encapsulated within VLAN 100, and
vlan && vlan 300 && ip
filters IPv4 protocols encapsulated in VLAN 300 encapsulated within any higher order VLAN.
mpls [label_num]
True if the packet is an MPLS packet. If [label_num] is specified, only true is the packet has the specified label_num. Note that the first mpls keyword encountered in expression changes the decoding offsets for the remainder of expression on the assumption that the packet is a MPLS-encapsulated IP packet. The mpls [label_num] expression may be used more than once, to filter on MPLS hierarchies. Each use of that expression increments the filter offsets by 4.
For example:
mpls 100000 && mpls 1024
filters packets with an outer label of 100000 and an inner label of 1024, and
mpls && mpls 1024 && host 192.9.200.1
filters packets to or from 192.9.200.1 with an inner label of 1024 and any outer label.
pppoed
True if the packet is a PPP-over-Ethernet Discovery packet (Ethernet type 0x8863).
pppoes
True if the packet is a PPP-over-Ethernet Session packet (Ethernet type 0x8864). Note that the first pppoes keyword encountered in expression changes the decoding offsets for the remainder of expression on the assumption that the packet is a PPPoE session packet.
For example:
pppoes && ip
filters IPv4 protocols encapsulated in PPPoE.
tcp, udp, icmp
Abbreviations for:
ip proto p or ip6 proto p
where p is one of the above protocols.
iso proto protocol
True if the packet is an OSI packet of protocol type protocol. Protocol can be a number or one of the names clnp, esis, or isis.
clnp, esis, isis
Abbreviations for:
iso proto p
where p is one of the above protocols.
l1, l2, iih, lsp, snp, csnp, psnp
Abbreviations for IS-IS PDU types.
vpi n
True if the packet is an ATM packet, for SunATM on Solaris, with a virtual path identifier of n.
vci n
True if the packet is an ATM packet, for SunATM on Solaris, with a virtual channel identifier of n.
lane
True if the packet is an ATM packet, for SunATM on Solaris, and is an ATM LANE packet. Note that the first lane keyword encountered in expression changes the tests done in the remainder of expression on the assumption that the packet is either a LANE emulated Ethernet packet or a LANE LE Control packet. If lane isn't specified, the tests are done under the assumption that the packet is an LLC-encapsulated packet.
llc
True if the packet is an ATM packet, for SunATM on Solaris, and is an LLC-encapsulated packet.
oamf4s
True if the packet is an ATM packet, for SunATM on Solaris, and is a segment OAM F4 flow cell (VPI=0 & VCI=3).
oamf4e
True if the packet is an ATM packet, for SunATM on Solaris, and is an end-to-end OAM F4 flow cell (VPI=0 & VCI=4).
oamf4
True if the packet is an ATM packet, for SunATM on Solaris, and is a segment or end-to-end OAM F4 flow cell (VPI=0 & (VCI=3 | VCI=4)).
oam
True if the packet is an ATM packet, for SunATM on Solaris, and is a segment or end-to-end OAM F4 flow cell (VPI=0 & (VCI=3 | VCI=4)).
metac
True if the packet is an ATM packet, for SunATM on Solaris, and is on a meta signaling circuit (VPI=0 & VCI=1).
bcc
True if the packet is an ATM packet, for SunATM on Solaris, and is on a broadcast signaling circuit (VPI=0 & VCI=2).
sc
True if the packet is an ATM packet, for SunATM on Solaris, and is on a signaling circuit (VPI=0 & VCI=5).
ilmic
True if the packet is an ATM packet, for SunATM on Solaris, and is on an ILMI circuit (VPI=0 & VCI=16).
connectmsg
True if the packet is an ATM packet, for SunATM on Solaris, and is on a signaling circuit and is a Q.2931 Setup, Call Proceeding, Connect, Connect Ack, Release, or Release Done message.
metaconnect
True if the packet is an ATM packet, for SunATM on Solaris, and is on a meta signaling circuit and is a Q.2931 Setup, Call Proceeding, Connect, Release, or Release Done message.
expr relop expr
True if the relation holds, where relop is one of >, <, >=, <=, =, !=, and expr is an arithmetic expression composed of integer constants (expressed in standard C syntax), the normal binary operators [+, -, *, /, &, |, <<, >>], a length operator, and special packet data accessors. Note that all comparisons are unsigned, so that, for example, 0x80000000 and 0xffffffff are > 0. To access data inside the packet, use the following syntax:
proto [ expr : size ]
Proto is one of ether, fddi, tr, wlan, ppp, slip, link, ip, arp, rarp, tcp, udp, icmp, ip6 or radio, and indicates the protocol layer for the index operation. (ether, fddi, wlan, tr, ppp, slip and link all refer to the link layer. radio refers to the "radio header" added to some 802.11 captures.) Note that tcp, udp and other upper-layer protocol types only apply to IPv4, not IPv6 (this will be fixed in the future). The byte offset, relative to the indicated protocol layer, is given by expr. Size is optional and indicates the number of bytes in the field of interest; it can be either one, two, or four, and defaults to one. The length operator, indicated by the keyword len, gives the length of the packet.

For example, `ether[0] & 1 != 0' catches all multicast traffic. The expression `ip[0] & 0xf != 5' catches all IPv4 packets with options. The expression `ip[6:2] & 0x1fff = 0' catches only unfragmented IPv4 datagrams and frag zero of fragmented IPv4 datagrams. This check is implicitly applied to the tcp and udp index operations. For instance, tcp[0] always means the first byte of the TCP header, and never means the first byte of an intervening fragment.

Some offsets and field values may be expressed as names rather than as numeric values. The following protocol header field offsets are available: icmptype (ICMP type field), icmpcode (ICMP code field), and tcpflags (TCP flags field).

The following ICMP type field values are available: icmp-echoreply, icmp-unreach, icmp-sourcequench, icmp-redirect, icmp-echo, icmp-routeradvert, icmp-routersolicit, icmp-timxceed, icmp-paramprob, icmp-tstamp, icmp-tstampreply, icmp-ireq, icmp-ireqreply,icmp-maskreq, icmp-maskreply.

The following TCP flags field values are available: tcp-fin, tcp-syn, tcp-rst, tcp-push, tcp-ack, tcp-urg.

Primitives may be combined using:

A parenthesized group of primitives and operators (parentheses are special to the Shell and must be escaped).
Negation (`!' or `not').
Concatenation (`&&' or `and').
Alternation (`||' or `or').

Negation has highest precedence. Alternation and concatenation have equal precedence and associate left to right. Note that explicit and tokens, not juxtaposition, are now required for concatenation.

If an identifier is given without a keyword, the most recent keyword is assumed. For example,

not host vs and ace
is short for
not host vs and host ace
which should not be confused with
not ( host vs or ace )

Expression arguments can be passed to tcpdump as either a single argument or as multiple arguments, whichever is more convenient. Generally, if the expression contains Shell metacharacters, it is easier to pass it as a single, quoted argument. Multiple arguments are concatenated with spaces before being parsed.  

EXAMPLES

To capture all packets arriving at or departing from sundown:

host sundown

To capture traffic between helios and either hot or ace:

host helios and \( hot or ace \)

To capture all IP packets between ace and any host except helios:

ip host ace and not helios

To capture all traffic between local hosts and hosts at Berkeley:

net ucb-ether

To capture all ftp traffic through internet gateway snup: (note that the expression is quoted to prevent the shell from (mis-)interpreting the parentheses):

gateway snup and (port ftp or ftp-data)

To capture traffic neither sourced from nor destined for local hosts (if you gateway to one other net, this stuff should never make it onto your local net).

ip and not net localnet

To capture the start and end packets (the SYN and FIN packets) of each TCP conversation that involves a non-local host.

tcp[tcpflags] & (tcp-syn|tcp-fin) != 0 and not src and dst net localnet

To capture all IPv4 HTTP packets to and from port 80, i.e. print only packets that contain data, not, for example, SYN and FIN packets and ACK-only packets. (IPv6 is left as an exercise for the reader.)

tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)

To capture IP packets longer than 576 bytes sent through gateway snup:

gateway snup and ip[2:2] > 576

To capture IP broadcast or multicast packets that were not sent via Ethernet broadcast or multicast:

ether[0] & 1 = 0 and ip[16] >= 224

To capture all ICMP packets that are not echo requests/replies (i.e., not ping packets):

icmp[icmptype] != icmp-echo and icmp[icmptype] != icmp-echoreply

This was taken from the man page of tcpdump.

크리에이티브 커먼즈 라이센스
Creative Commons License
2012/04/09 18:26 2012/04/09 18:26
받은 트랙백이 없고, 댓글이 없습니다.

댓글+트랙백 RSS :: http://blog.hwport.com/rss/response/652

댓글+트랙백 ATOM :: http://blog.hwport.com/atom/response/652


본 내용은 "http://www.freedesktop.org/wiki/Software/Xserver/InstallGuide?action=print" 에서 발췌한 것임을 밝힙니다.


----


Software/Xserver/InstallGuide

These instructions are for installing kdrive-based X Servers on your system. This does not apply to X.Org at all.

If you're looking for an X server with transparency support, you might consider X.Org since its transparency support is rapidly stabilizing.

If you have some other reason for using the kdrive based server covered here, I'm looking for a replacement maintainer for this page....

It is surprisingly difficult to follow this type of instruction set. You'll have to concentrate and double-check yourself constantly. Good luck!

Unless you're planning to contribute code, you'll usually be better off using packages from your distribution if it has any. Some do. If yours is one of them, feel free and note it here or report it to me (see address at bottom of page.)

These instructions only go as far as the X server and xcompmgr (the eye-candy maker) and the things they require.

Before You Build

  • autoconf --version must report 2.59 or later

  • automake --version must report 1.9.x

  • libtool --version must report 1.5 (available from http://ftp.gnu.org/gnu/libtool/ if your distribution doesn't have it)

  • pkg-config --version must report 0.9.0 or later

Troubleshooting

If you're getting errors mentioning automake, check that your automake install is recent enough using the commands above.

If you're getting errors mentioning PKG_CHECK_MODULES(), install pkgconfig.

You might also try XserverBuildScript which contains all these instructions in a runnable form.

The KDrive xserver isn't supported in FreeBSD. Anyone interested in porting it is welcome to do so, though.

Note that xcompmgr and window borders don't mix. Most window managers disable window borders but twm users may find that BorderWidth 0 in ~/.twmrc helps.

In case you get an error while compiling such as this:

checking for pkg-config... /opt/gnome2/bin/pkg-config
checking pkg-config is at least version 0.9.0... yes
checking for XDMCP... yes
checking for XdmcpWrap in -lXdmcp... yes
checking for XEPHYR... yes
checking for XSERVER... configure: error: Package requirements (randrproto renderproto fixesproto damageproto xextproto xfont xproto xtrans xau compositeproto resourceproto recordproto xdmcp xdmcp) were not met.
Consider adjusting the PKG_CONFIG_PATH environment variable if you
installed software in a non-standard prefix.
 
Alternatively you may set the XSERVER_CFLAGS and XSERVER_LIBS environment variables
to avoid the need to call pkg-config.  See the pkg-config man page for
more details.
*** error during stage configure of xserver: could not configure module *** [32/38]

You might want to try this workaround:

cd /opt/fdo/lib/pkgconfig
ln -s randr.pc randrproto.pc
ln -s render.pc renderproto.pc
ln -s fixesext.pc fixesproto.pc
ln -s damageext.pc damageproto.pc
ln -s xext.pc xextproto.pc
ln -s compositeext.pc compositeproto.pc
ln -s resourceext.pc resourceproto.pc
ln -s recordext.pc recordproto.pc

And rerun the build. It helped here, but it's just a workaround, the real problem is somewhere else. If anyone knows better please fix the real problem instead.

Build Commands

Script version of these commands: XserverBuildScript

cvs login is no longer required when using cvs.freedesktop.org.

if [ -z "$PKG_CONFIG_PATH" ]; then
export PKG_CONFIG_PATH=/opt/fdo/lib/pkgconfig
else
export PKG_CONFIG_PATH="/opt/fdo/lib/pkgconfig:$PKG_CONFIG_PATH"
fi

cvs -d :pserver:anoncvs@cvs.freedesktop.org:/cvs/xlibs co Xproto
pushd Xproto
./autogen.sh --prefix=/opt/fdo
make
sudo make install
popd

cvs -d :pserver:anoncvs@cvs.freedesktop.org:/cvs/xlibs co Xdmcp
pushd Xdmcp
./autogen.sh --prefix=/opt/fdo
make
sudo make install
popd

cvs -d :pserver:anoncvs@cvs.freedesktop.org:/cvs/xlibs co XExtensions
pushd XExtensions
./autogen.sh --prefix=/opt/fdo
make
sudo make install
popd

cvs -d :pserver:anoncvs@cvs.freedesktop.org:/cvs/xlibs co xtrans
pushd xtrans
./autogen.sh --prefix=/opt/fdo
make
sudo make install
popd

cvs -d :pserver:anoncvs@cvs.freedesktop.org:/cvs/xlibs co Xau
pushd Xau
./autogen.sh --prefix=/opt/fdo
make
sudo make install
popd

cvs -d :pserver:anoncvs@cvs.freedesktop.org:/cvs/xlibs co X11
pushd X11
./autogen.sh --prefix=/opt/fdo
make
sudo make install
popd

cvs -d :pserver:anoncvs@cvs.freedesktop.org:/cvs/xlibs co Xext
pushd Xext
./autogen.sh --prefix=/opt/fdo
make
sudo make install
popd

cvs -d :pserver:anoncvs@cvs.freedesktop.org:/cvs/xlibs co Randr
pushd Randr
./autogen.sh --prefix=/opt/fdo
make
sudo make install
popd

cvs -d :pserver:anoncvs@cvs.freedesktop.org:/cvs/xlibs co Render
pushd Render
./autogen.sh --prefix=/opt/fdo
make
sudo make install
popd

cvs -d :pserver:anoncvs@cvs.freedesktop.org:/cvs/xlibs co Xrender
pushd Xrender
./autogen.sh --prefix=/opt/fdo
make
sudo make install
popd

cvs -d :pserver:anoncvs@cvs.freedesktop.org:/cvs/xlibs co Xrandr
pushd Xrandr
./autogen.sh --prefix=/opt/fdo
make
sudo make install
popd

cvs -d :pserver:anoncvs@cvs.freedesktop.org:/cvs/xlibs co FixesExt
pushd FixesExt
./autogen.sh --prefix=/opt/fdo
make
sudo make install
popd

cvs -d :pserver:anoncvs@cvs.freedesktop.org:/cvs/xlibs co DamageExt
pushd DamageExt
./autogen.sh --prefix=/opt/fdo
make
sudo make install
popd

cvs -d :pserver:anoncvs@cvs.freedesktop.org:/cvs/xlibs co Xfont
pushd Xfont
./autogen.sh --prefix=/opt/fdo
make
sudo make install
popd

cvs -d :pserver:anoncvs@cvs.freedesktop.org:/cvs/xlibs co ResourceExt
pushd ResourceExt
./autogen.sh --prefix=/opt/fdo
make
sudo make install
popd

cvs -d :pserver:anoncvs@cvs.freedesktop.org:/cvs/xlibs co RecordExt
pushd RecordExt
./autogen.sh --prefix=/opt/fdo
make
sudo make install
popd

cvs -d :pserver:anoncvs@cvs.freedesktop.org:/cvs/xlibs co CompositeExt
pushd CompositeExt
./autogen.sh --prefix=/opt/fdo
make
sudo make install
popd

cvs -d :pserver:anoncvs@cvs.freedesktop.org:/cvs/xlibs co xkbfile
pushd xkbfile
./autogen.sh --prefix=/opt/fdo
make
sudo make install
popd

cvs -d :pserver:anoncvs@cvs.freedesktop.org:/cvs/xserver co xserver
pushd xserver
./autogen.sh --prefix=/opt/fdo --enable-composite
make
sudo install -d /opt/fdo/bin
sudo make install
popd

cvs -d :pserver:anoncvs@cvs.freedesktop.org:/cvs/xlibs co Xfixes
pushd Xfixes
./autogen.sh --prefix=/opt/fdo
make
sudo make install
popd

cvs -d :pserver:anoncvs@cvs.freedesktop.org:/cvs/xlibs co Xcomposite
pushd Xcomposite
./autogen.sh --prefix=/opt/fdo
make
sudo make install
popd

cvs -d :pserver:anoncvs@cvs.freedesktop.org:/cvs/xlibs co Xdamage
pushd Xdamage
./autogen.sh --prefix=/opt/fdo
make
sudo make install
popd

# http://cvs.freedesktop.org/xapps/
wget http://www.freedesktop.org/xapps/release/xcompmgr-1.1.tar.gz
tar zxf xcompmgr-1.1.tar.gz
pushd xcompmgr-1.1
./autogen.sh --prefix=/opt/fdo
make
sudo make install
popd

# to start server, try something like...
sudo /opt/fdo/bin/Xvesa :1 & xterm -display :1

# in xterm...
export LD_LIBRARY_PATH="/opt/fdo/lib:$LD_LIBRARY_PATH"
/opt/fdo/bin/xcompmgr

I use pushd/popd instead of cd/cd .. because it protects against bugs in those install instructions that require directory changes. Fortunately, these are well behaved packages with simple installs.

Interesting XServer Options

  • -mouse /dev/psaux,5 enables the scroll wheel

  • -mouse /dev/input/mice,5 for a usb mouse with scroll wheel

  • -nolisten tcp if you're getting xtrans errors about inet6 or other socket setup things.

  • -ac -nolisten tcp if you're getting access denied errors when trying to start xterm. Beware this is a security risk, even with -nolisten tcp, so don't do it on a multi-user machine. I've heard some mention that you won't need this if your machine's hostname is set up correctly.

  • -listmodes lists modes the VESA driver supports

  • -help lists all these and more

  • Don't forget the man pages :)

Contacting the Author

This page was originally written by SethKlein on 12-NOV-2003. He no longer uses kdrive and so as of 29-DEC-2004 this page is unmaintained. Please feel free to take up maintainership if you find it useful.

Updated by DiegoEscalante on 10-FEB-2006 while trying to get Xgl working.

Software/Xserver/InstallGuide (2008-08-23 14:10:37에 localhost가(이) 마지막으로 수정)

크리에이티브 커먼즈 라이센스
Creative Commons License
2012/04/05 10:28 2012/04/05 10:28
받은 트랙백이 없고, 댓글이 없습니다.

댓글+트랙백 RSS :: http://blog.hwport.com/rss/response/649

댓글+트랙백 ATOM :: http://blog.hwport.com/atom/response/649


본 내용은 "http://lists.kde.org/?l=kde-devel&m=124264501607353" 에서 발췌한 것임을 밝힙니다.

----
List:       kde-devel
Subject:    Re: Where to start for making an alpha blended window thingy
From:       Shashank Singh <shashank.personal () gmail ! com>
Date:       2009-05-18 11:20:33
Message-ID: 99584b820905180408i26b19ebja70d1ba5aaaad142 () mail ! gmail ! com
[Download message RAW]

[Attachment #2 (multipart/alternative)]


Hi,
if you want to make a ARGB window using xlib/Qt here is the code i use
/**files to include from xlib*/
#include <X11/extensions/Xrender.h>
#include <X11/Xlib.h>

/**getting display*/
Display *dpy = XOpenDisplay ( 0 ); // open default display
    if ( !dpy ) {
        qWarning ( "Cannot connect to the X server" );
        exit ( 1 );
    }

/**code for argb visual*/
    bool  argbVisual=false;
    int screen = DefaultScreen ( dpy );
    Colormap colormap = 0;
    Visual *visual = 0;
    int eventBase, errorBase;

    if ( XRenderQueryExtension ( dpy, &eventBase, &errorBase ) ) {
        qWarning ("You have ARGB support");
        int nvi;
        XVisualInfo templ;
        templ.screen  = screen;
        templ.depth   = 32;
        templ.c_class = TrueColor;
        XVisualInfo *xvi = XGetVisualInfo ( dpy, VisualScreenMask |
                                            VisualDepthMask |
                                            VisualClassMask, &templ, &nvi );

        for ( int i = 0; i < nvi; ++i ) {
            XRenderPictFormat *format = XRenderFindVisualFormat ( dpy,
                                        xvi[i].visual );
            if ( format->type == PictTypeDirect && format->direct.alphaMask
) {
                visual = xvi[i].visual;
                colormap = XCreateColormap ( dpy, RootWindow ( dpy, screen
),
                                             visual, AllocNone );
                argbVisual=true;
                break;
            }
        }
    }
    /**end of code for argb visual*/

then QApplication app (dpy, argc, argv,Qt::HANDLE ( visual ), Qt::HANDLE (
colormap ) );



On Thu, May 14, 2009 at 6:24 PM, Thomas Lübking <thomas.luebking@web.de>wrote:

> Am Thursday 14 May 2009 schrieb Harald Hvaal:
>
>
> > Do I need to write graphics routines through kwin plugin framework? Is
> if you want an effect on desktops/windows (like the cube switch etc.)
> that's likely the best location
>
>
> > there a flag in Qt that I can use to get an alpha blended backgroudn for
> a
> > qmainwindow?
> Qt 4.5 (only on window type widgets QWidget::isWindow())
> QWidget::setAttribute(Qt::WA_TranslucentBackground)
> this will however (obviously) only work on composited desktops
>
>
> > Maybe plasma has something for this? Is it even possible?
> if you want whatever you want on the desktop layer (i.e. below all normal
> windows), you can make a plasmoid (QGraphicsViewItem) and just use alpha
> blending
>
>
> Thomas
>
>
>
>
>
> >> Visit http://mail.kde.org/mailman/listinfo/kde-devel#unsub to
> unsubscribe <<
>
>


-- 
Regards:
Shashank Singh
Associate - Software | New Technologies team
Geodesic Limited |  <http://www.geodesic.com>
Tel: +91 22 2831 2872

Blog:http://techfreaks4u.com/blog
Marble contributor : http://edu.kde.org/marble/
KDE-in Volunteer : http://www.kde.in/

[Attachment #5 (text/html)]

Hi,<br>if you want to make a ARGB window using xlib/Qt here is the code i \
use<br>/**files to include from xlib*/<br>#include \
&lt;X11/extensions/Xrender.h&gt;<br>#include &lt;X11/Xlib.h&gt;<br>    <br>/**getting \
display*/<br>

Display *dpy = XOpenDisplay ( 0 ); // open default display<br>    if ( !dpy ) \
{<br>        qWarning ( &quot;Cannot connect to the X server&quot; );<br>        exit \
( 1 );<br>    }<br><br>/**code for argb visual*/<br>    bool  argbVisual=false;<br>

    int screen = DefaultScreen ( dpy );<br>    Colormap colormap = 0;<br>    Visual \
*visual = 0;<br>    int eventBase, errorBase;<br><br>    if ( XRenderQueryExtension ( \
dpy, &amp;eventBase, &amp;errorBase ) ) {<br>        qWarning (&quot;You have ARGB \
support&quot;);<br>

        int nvi;<br>        XVisualInfo templ;<br>        templ.screen  = \
screen;<br>        templ.depth   = 32;<br>        templ.c_class = \
TrueColor;<br>        XVisualInfo *xvi = XGetVisualInfo ( dpy, VisualScreenMask |<br>

                                            VisualDepthMask \
|<br>                                            VisualClassMask, &amp;templ, \
&amp;nvi );<br><br>        for ( int i = 0; i &lt; nvi; ++i ) {<br>            \
XRenderPictFormat *format = XRenderFindVisualFormat ( dpy,<br>

                                        xvi[i].visual );<br>            if ( \
format-&gt;type == PictTypeDirect &amp;&amp; format-&gt;direct.alphaMask ) \
{<br>                visual = xvi[i].visual;<br>                colormap = \
XCreateColormap ( dpy, RootWindow ( dpy, screen ),<br>

                                             visual, AllocNone );<br>                \
argbVisual=true;<br>                break;<br>            }<br>        }<br>    \
}<br>    /**end of code for argb visual*/<br><br>then QApplication app (dpy, argc, \
argv,Qt::HANDLE ( visual ), Qt::HANDLE ( colormap ) );<br>

<br><br><br><div class="gmail_quote">On Thu, May 14, 2009 at 6:24 PM, Thomas Lübking \
<span dir="ltr">&lt;<a \
href="mailto:thomas.luebking@web.de">thomas.luebking@web.de</a>&gt;</span> \
wrote:<br><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, \
204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">

<div style="font-family: &#39;Segoe&#39;; font-size: 10pt; font-weight: 400; \
font-style: normal;">Am Thursday 14 May 2009 schrieb Harald Hvaal:<div \
class="im"><br> <p style="margin: 0px; text-indent: 0px;"><br></p>&gt; Do I need to \
write graphics routines through kwin plugin framework? Is<br></div> if you want an \
effect on desktops/windows (like the cube switch etc.) that&#39;s likely the best \
location<div class="im"><br> <p style="margin: 0px; text-indent: 0px;"><br></p>&gt; \
there a flag in Qt that I can use to get an alpha blended backgroudn for a<br> &gt; \
qmainwindow? <br></div> Qt 4.5 (only on window type widgets QWidget::isWindow())<br>
QWidget::setAttribute(Qt::WA_TranslucentBackground)<br>
this will however (obviously) only work on composited desktops<div class="im"><br>
<p style="margin: 0px; text-indent: 0px;"><br></p>&gt; Maybe plasma has something for \
this? Is it even possible?<br></div> if you want whatever you want on the desktop \
layer (i.e. below all normal windows), you can make a plasmoid (QGraphicsViewItem) \
and just use alpha blending<br><font color="#888888"> <p style="margin: 0px; \
text-indent: 0px;"><br></p>Thomas<br> <p style="margin: 0px; text-indent: \
0px;"><br></p><p style="margin: 0px; text-indent: 0px;"><br></p></font></div><br><br> \
&gt;&gt; Visit <a href="http://mail.kde.org/mailman/listinfo/kde-devel#unsub" \
target="_blank">http://mail.kde.org/mailman/listinfo/kde-devel#unsub</a> to \
unsubscribe &lt;&lt;<br> <br></blockquote></div><br><br clear="all"><br>-- \
<br>Regards:<br>Shashank Singh<br>Associate - Software | New Technologies \
team<br>Geodesic Limited |  &lt;<a \
href="http://www.geodesic.com">http://www.geodesic.com</a>&gt; <br>

Tel: +91 22 2831 2872 <br><br>Blog:<a \
href="http://techfreaks4u.com/blog">http://techfreaks4u.com/blog</a><br>Marble \
contributor : <a href="http://edu.kde.org/marble/">http://edu.kde.org/marble/</a><br>K \
DE-in Volunteer : <a href="http://www.kde.in/">http://www.kde.in/</a><br>



>> Visit http://mail.kde.org/mailman/listinfo/kde-devel#unsub to unsubscribe <<




크리에이티브 커먼즈 라이센스
Creative Commons License
2012/04/05 10:24 2012/04/05 10:24
받은 트랙백이 없고, 댓글이 없습니다.

댓글+트랙백 RSS :: http://blog.hwport.com/rss/response/648

댓글+트랙백 ATOM :: http://blog.hwport.com/atom/response/648


본 내용은 "http://en.gentoo-wiki.com/w/index.php?title=X.Org/Transparency&printable=yes" 에서 발췌한 것임을 밝힙니다.


----



X.Org/Transparency

From Gentoo Linux Wiki

X.Org TOC

This guide is meant to explain how to enable transparency effects in various Window Managers and how to use other X tools for greater control.

Contents

X Composite Extension

It's essential that you first configure the X server to enable its Xcomposite extension. Add the following to /etc/X11/xorg.conf,

File: /etc/X11/xorg.conf
Section "Extensions"
    Option "Composite" "Enable"
EndSection

Restart the X server for these settings to take effect.

Note: You can verify that the Xcomposite extension is enabled with,
xdpyinfo | grep Composite
Emerge x11-apps/xdpyinfofor the xdpyinfo application.

Enabling Shadows and Real Transparency

KDE4

KDE4's KWin4 Window Manger has built-in 3D-plugins, much like Compiz. Emerge KDE's packages with the xcomposite, where applicable, enabled. See System Settings -> Desktop to enable and configure KDE's Desktop Effects.

KDE 3.x

KDE 3.x's KWin Window Manager has support for composite features provided you set the xcomposite USE-flag when emerging kde-base/kwinand kde-base/kickerfor the split ebuilds or kde-base/kdebaseusing the monolithic ebuilds. Open up KDE Control Center from the KMenu. On the tree on the left, click Desktop->Window Behavior. Click on the Translucency tab and check the Use translucency/shadows option. The various settings are then available for tweaking in the configuration frame. Restarting KDE may be required to enable your new settings.

Note: Make sure you turn off the standard KDE GUI effects (Control Center->Appearance & Themes->Style->Effects->Enable GUI Effects), otherwise the effects will double up and cause strange things to happen.

Enlightenment 0.16

Enlightenment 0.16 supports the composite extension natively, including transparency and shadows and it requires no special USE-flag. In the default configuration it is not enabled, you have to right-click on the desktop and select Composite Settings. Now you are able to control the features for the different windows by right-clicking on the titlebar (the menu where you can set all differnt kinds of window properties. No restart is required.

Enlightenment DR17

Enlightenment DR17 cvs does have a module called Bling for controlling drop shadows and window fades. Just unmask and emerge e_modulesafter emerging e. The module can be activated from Configuration->Modules->Bling.

It is crucial that you issue

enlightenment_remote -use-composite-set 1

as the user who is running e17.

You will also have to deactivate the Dropshadow module in order to get nice shadows which do not interfere with the Bling shadows.

Xfce

Settings -> Window Manager Tweaks -> Compositor

Tools

Fix me: Explain usage of Cairo-Composite-Manager instead of XCompMgr.

xcompmgr

Warning: xcompmgr is a proof of concept application and is not meant for daily use. It is known to have memory leak issues, see bug 150348.
emerge -a xcompmgr

At this point we have all we need to enable drop shadows, cool "fade out" effects and real transparency. First we need to launch xcompmgr. From a console inside of X.Org do this:

xcompmgr -c

This will let you see the effects of running xcompmgr - if you don't like them, just type Ctrl+c from the console you are running xcompmgr in. If you do like them, type Ctrl+c and then:

xcompmgr -c &

The & asks xcompmgr to run in the background - when it's started this way you will have to use kill or killall to stop xcompmgr.

Note: Rather than doing Ctrl+c and killing the program you can also Crtl+z to suspend the program momentarily and type bg to put the process in the background without having to use &. Background jobs can be listed with the jobs command and killed with thekill %1 command where 1 is the number listed in the 'jobs' command output. Note that after suspending composite manager your screen will freeze until you type 'bg'.

This will enable xcompmgr with soft drop shadows turned on. You should now have happy little drop shadows on all your windows. You could optionally use the -s flag instead for hard shadows.

Here are some example settings for xcompmgr with shadow and fading,

xcompmgr -cCfF -r7 -o.65 -l-10 -t-8 -D7 &

To disable all of this after you have got it going, simply type this in a console,

killall xcompmgr

This will turn xcompmgr off, and disable all drop shadows and transparency settings.

transset

Note: You will need to have xcompmgr running for transset to work.

To enable transparent window, you'll need to tool x11-misc/transset,

emerge -a transset
Note: You will need to have xcompmgr running for transset to work.

Run in a terminal,

transset

Now your cursor will have changed, simply click on the window you want to make see through. You can also pass transset a value to define how transparent to make the windows, where 0 is fully transparent and 1 is fully opaque. Try:

transset .2

transset-df

Note: You will need to have xcompmgr running for transset-df to work.

If you don't want to wait and transset is not too handy for you, you can use x11-misc/transset-df,

emerge -a transset-df

Then, you can use keyboard or mouse shortcuts to set the transparency value to the focused windows.

The scroll wheel method can be realized via xbindkeys.

emerge -a xbindkeys
File: ~/.xbindkeysrc
"transset-df --min 0.1 -p --dec 0.2"
 control + b:4
"transset-df -p --inc 0.1"
 control + b:5

This way you can adjust the transparency of any window dynamically.

Troubleshooting

Performance

To increase performance, add to your /etc/X11/xorg.conf,

File: /etc/X11/xorg.conf
Section "Device"
    ...
    Option "RenderAccel" "True"
EndSection
Note: Acceleration currently does not work if Nvidia TwinView is being used.

Another preformance tip is enabling Backing Store, add to your /etc/X11/xorg.conf,

Warning: Do not enable BackingStore with KDE 4.x, X server will crash!
File: /etc/X11/xorg.conf
Section "ServerLayout"
    ...
    Option "BackingStore" "True"
EndSection

When enabled, you will find the following lines in your xorg log file /var/log/Xorg.0.log:

File: /var/log/Xorg.0.log
(**) (0): Option "BackingStore" "true"
(**) (0): Backing store enabled"

This option is used to enable the server's support for backing store, a mechanism by which pixel data for occluded window regions is remembered by the server thereby alleviating the need to send expose events to X clients when the data needs to be redisplayed. This can significantly increase performance.

크리에이티브 커먼즈 라이센스
Creative Commons License
2012/04/05 10:10 2012/04/05 10:10
받은 트랙백이 없고, 댓글이 없습니다.

댓글+트랙백 RSS :: http://blog.hwport.com/rss/response/647

댓글+트랙백 ATOM :: http://blog.hwport.com/atom/response/647


본 내용은 "http://www.cisco.com/en/US/prod/collateral/iosswrel/ps6537/ps6553/white_paper_c11-676277.html" 에서 발췌한 글임을 밝힙니다.


----


Within the IPv4 networked world Network Address and Port Translation (NAPT, but also called NAT) between a public IPv4 address and a private IPv4 address was created around 2001 to address the ongoing depletion of the public IPv4 address pool. Another solution to resolve address depletion was a technology identified as VLSM (variable length subnet Masks) and served as a good short term solution. The long term solution for IPv4 address exhaust resulted in a forklift IP upgrade from IPv4 towards IPv6.
This paper will discuss briefly traditional IPv4 address translation, followed with some elements of cross IPv4 and IPv6 NAT translation solutions, including the historical NAT-PT solution.
What is in this paper:

• IPv4-IPv4 Address translation-NAPT (also known as NAT44)

• NAT-PT-(IPv6 Network Address Translation-Protocol Translation)

• IPv6 to IPv4 Network Address Translation

– Stateless NAT64 translation

– Statefull NAT64 translation

IPv4 Network Address and Port Translation (IPv4 NAT and NAPT)

The IPv4 network address and port translation (NAPT, RFC3022) we all know is mainly based upon private IPv4 addresses space towards public IPv4 address space. This is utilized most frequently at residential Home-Gateways during last decade and is a stateful technology. It is stateful because each translation creates state within the NAPT device, necessary for the return packet to be routed. The initial purpose of NAPT was to multiplex many users in a single IPv4 address on the global Internet. The example shown here is with Cisco NAT (with overload technology) where many devices using private RFC1918 addresses on the inside of the NAT44 can use a single global unique IPv4 address to communicate on the global IPv4 Internet. The Cisco Command Line Interface (CLI) shown below gives an indication of the stateful mapping between the inside and outside IP addresses and port numbers used.

Table 1. Stateful Mapping Between the Inside and Outside IP Addresses and Port Numbers

Router# show ip nat translations
Pro Inside global Inside local Outside local Outside global
udp 172.16.233.209:1220 192.168.1.95:1220 172.16.2.132:53 172.16.2.132:53
tcp 172.16.233.209:11012 192.168.1.89:11012 172.16.1.220:23 172.16.1.220:23
tcp 172.16.233.209:1067 192.168.1.95:1067 172.16.1.161:23 172.16.1.161:23
The usage of an IPv4 Network Address Translator device goes from the assumption that the L4 protocol used is either UDP or TCP, and that the two communicating end-stations support IPv4 as protocol.
An important issue of NAPT isn't so much on "what amount of memory do you need" or "what is the lookup time in the implied data structure". The issue is the effect on applications. As a result of NAPT, we can't put publicly-accessible applications behind the firewall, and have to think about their security separately.

NAT-PT-(IPv6 Network Address Translation-Protocol Translation)

The first solution the IETF (Internet Engineering Task Force-www.ietf.org) developed to allow IPv4 `ONLY' hosts to speak to IPv6 `ONLY' hosts and vice versa is known as NAT-PT (RFC2766). This first translation solution between IPv4 and IPv6 existed out of mainly two parts:

• Domain Name System (DNS) Application Level Gateway (ALG)

– When a IPv6 user does a DNS name query for a device with only IPv4 connectivity (ie. www.foo4.com), then the DNS request must be translated from being a IPv6 DNS request towards a IPv4 DNS request. This is a function achieved by the DNS ALG embedded within the NAT-PT technology.

– Similar when a IPv4 only user does a DNS name query for a device with only IPv6 connectivity the DNS ALG embedded in the NAT-PT translates this DNS request

• Address Family Translation (AFT)

– When an IPv6-only device wants to send an IPv6 data packet to an IPv4-only device then, there is need for a translation of the IPv6 protocol header into a IPv4 protocol header so that the recipient device can understand the data. On the return path after the IPv4-only device sends a response packet, a translation is needed from IPv4 towards IPv6, so that the IPv6-only device can understand the response packet.

– The foundational algorithm of the IPv4/IPv6 protocol translation used within NAT-PT is based upon a technology called "Stateless IP/ICMP Translator" also known as SIIT (RFC2765). This same technology describes also the IPv6-initiated IPv6-to-IPv4 protocol translation known as stateless NAT64.

Over the years NAT-PT usage has proven as technology to be too complex to maintain scalable translational services, resulting in it being declared historical (RFC4966-Reasons to Move the Network Address Translator-Protocol Translator (NAT-PT) to Historic Status). However, translational technology has been made more modular and segmented, and replacements for translations between IPv6 and IPv4 have been created within a focussed translation framework by the IETF in the Behave working group (http://datatracker.ietf.org/wg/behave/charter/).

Stateless NAT64-Stateless translation between IPv4 and IPv6

RFC6145 (IP/ICMP Translation Algorithm) replaces RFC2765 (Stateless IP/ICMP Translation Algorithm (SIIT)) and provides a stateless mechanism to translate a IPv4 header into an IPv6 header and vice versa. Due to the stateless character this mechanism is very effective and highly fail safe because more as a single-or multiple translators in parallel can be deployed and work all in parallel without a need to synchronize between the translation devices.
The key to the stateless translation is in the fact that the IPv4 address is directly embedded in the IPv6 address. A limitation of stateless NAT64 translation is that it directly translates only the IPv4 options that have direct IPv6 counterparts, and that it does not translate any IPv6 extension headers beyond the fragmentation extension header; however, these limitations are not significant in practice.
With a stateless NAT64, a specific IPv6 address range will represent IPv4 systems within the IPv6 world. This range needs to be manually configured on the translation device. Within the IPv4 world all the IPv6 systems have directly correlated IPv4 addresses that can be algorithmically mapped to a subset of the service provider's IPv4 addresses. By means of this direct mapping algorithm there is no need to keep state for any translation slot between IPv4 and IPv6. This mapping algorithm requires the IPv6 hosts be assigned specific IPv6 addresses, using manual configuration or DHCPv6.
Stateless NAT64 will work very successful as proven in some of the largest networks, however it suffers from some an important side-effect: Stateless NAT64 translation will give an IPv6-only host access to the IPv4 world and vice versa, however it consumes an IPv4 address for each IPv6-only device that desires translation -- exactly the same as a dual-stack deployment. Consequentially, stateless NAT64 is no solution to address the ongoing IPv4 address depletion. Stateless NAT64 is a good tool to provide Internet servers with an accessible IP address for both IPv4 and IPv6 on the global Internet. To aggregate many IPv6 users into a single IPv4 address, stateful NAT64 is required.
NAT64 are usually deployed in conjunction with a DNS64. This functions similar to, but different than, DNS-ALG that was part of NAT-PT. DNS64 is not an ALG; instead, packets are sent directly to and received from the DNS64's IP address. DNS64 can also work with DNSSEC (whereas DNS-ALG could not).

Stateful NAT64-Network Address and Protocol Translation from IPv6 Clients to IPv4 Servers

Stateful NAT64 multiplexes many IPv6 devices into a single IPv4 address. It can be assumed that this technology will be used mainly where IPv6-only networks and clients (ie. Mobile handsets, IPv6 only wireless, etc...) need access to the IPv4 internet and its services.
The big difference with stateful NAT64 is the elimination of the algorithmic binding between the IPv6 address and the IPv4 address. In exchange, state is created in the NAT64 device for every flow. Additionally, NAT64 only supports IPv6-initiated flows. Unlike stateless NAT64, stateful NAT64 does `not' consume a single IPv4 address for each IPv6 device that wants to communicate to the IPv4 Internet. More practically this means that many IPv6-only users consume only single IPv4 address in similar manner as IPv4-to-IPv4 network address and port translation works. This works very well if the connectivity request is initiated from the IPv6 towards the IPv4 Internet. If an IPv4-only device wants to speak to an IPv6-only server for example, manual configuration of the translation slot will be required, making this mechanism less attractive to provide IPv6 services towards the IPv4 Internet.
DNS64 is usually also necessary with a stateful NAT64, and works the same with both stateless and stateful NAT64.

Summary

While stateful and stateless NAT64 perform the task of translating IPv4 packets into IPv6 packets and vice versa, there are important differences as explained in previous sections. The following table provides a high-level overview of the most relevant differences.

Table 2. Differences Between Stateless NAT64 and Stateful NAT64

Stateless NAT64

Stateful NAT64

1:1 translation

1:N translation

No conservation of IPv4 address

Conserves IPv4 address

Assures end-to-end address transparency and scalability

Uses address overloading, hence lacks in end-to-end address transparency

No state or bindings created on the translation

State or bindings are created on every unique translation

Requires IPv4-translatable IPv6 addresses assignment (mandatory requirement)

No requirement on the nature of IPv6 address assignment

Requires either manual or DHCPv6 based address assignment for IPv6 hosts

Free to choose any mode of IPv6 address assignment viz. Manual, DHCPv6, SLAAC

크리에이티브 커먼즈 라이센스
Creative Commons License
2012/04/04 00:22 2012/04/04 00:22
받은 트랙백이 없고, 댓글이 없습니다.

댓글+트랙백 RSS :: http://blog.hwport.com/rss/response/646

댓글+트랙백 ATOM :: http://blog.hwport.com/atom/response/646


본 내용은 "http://devpit.org/wiki/Gnu_Toolchain/Compatibility_Matrix" 에서 발췌한것입을 밝힙니다.


Gnu Toolchain/Compatibility Matrix

This page is designated for tabulating version dependencies between different toolchain packages in order to identify stable toolchains for release. It can also be used to outline version [in]compatibility issues e.g. GLIBC 2.4 will not work with gcc 3.4.3 because GLIBC 2.4 requires thread local storage (i.e. __THREAD) and GCC 3.4.3 doesn't provide TLS support.

Inter-Package Compatibility Matrix

GNU Toolchain Inter-Package Compatibility Matrix
VeinGCCGLIBCDescription
Thread Local StorageGCC Version 4.1 because GCC 3.4.3 does not provide TLS support.GLIBC Version 2.4 requires TLS support enabled using the flags: --with-tls --with-__threadGLIBC Version 2.4 (and later) requires TLS support. This means that GCC Version 3.4.3 will NOT work with GLIBC Version 2.4 (or later). The glibc make system will generate the following compilation error if TLS support is not enabled: 'error: #error "TLS support is required"'
-O2 OptimizationWithout optimizations enabled gcc will not inline functions.[1]GLIBC requires that the -O2[2] optimization CFLAG be set.The GLIBC make system will generate the following compilation error if optimization is not specified: '#error "glibc cannot be compiled without optimization"'
GCC moved limits.h and syslimits.hGCC Version 4.3 moved limit.s and syslimits.h to new include-fixed directoryGLIBC snapshot preceeding Jan 31, 2008 (i.e. GLIBC 2.7)GCC 4.3 added an include-fixed directory and moved limits.h and syslimits.h there [3]. Compiling GLIBC 2.7 (or prior) with GCC 4.3 will require these [4]patches to work.
VeinAutoconfGLIBCDescription
Configure scriptsAutoconf Version 2.61GLIBC Version 2.7GLIBC 2.7 had it's sysdep directory configure fragments rebuilt using Autoconf 2.61. This can cause problems if GLIBC determines it needs to re-generate the top level configure script on a system that has a version of Autoconf prior to Autoconf 2.61 installed[5].
  1. ^  From the GCC 4.2.1 manual: always_inline(function attribute):
    "Generally, functions are not inlined unless optimization is specified.
    For functions declared inline, this attribute inlines the function
    even if no optimization level was specified."
    • Given the following prototype:
    static inline function_name()
    • If explicit optimization wasn't specified then the function would not be inlined as necessary. One could direct GCC to inline even without the optimization flags using __attribute__((always_inline)) but for GLIBC this is never done, e.g.
    static inline function_name() __attribute__((always_inline))
  2. ^  libc-alpha Re: glibc only with optimization?
    "In the early startup of the dynamic loader (_dl_start), before
    relocation of the PLT, you cannot make function calls. You must inline
    the functions you will use during early startup, or call compiler
    builtins (__builtin_*).

    Without optimizations enabled gcc will not inline functions. The early
    startup of the dynamic loader will make function calls via an
    unrelocated PLT and crash." -- Carlos O'Donell
    glibc-bugs: Bug faq/5012 Why must glibc be compiled with optimizations enabled?
    "Without auditing the dynamic linker code it would be difficult to remove
    this requirement. In practice there is no reason to compile without
    optimizations, therefore we require that GNU libc be compiled with
    optimizations enabled." -- Carlos O'Donell
    glibc-bugs: Bug faq/5012 Why must glibc be compiled with optimizations enabled?
    "There are other reasons. For instance, nested functions must be inlined in many cases to avoid executable stacks. " -- Ulrich Drepper
  3. ^  From the libc-alpha mailing list: glibc --with-headers and GCC 4.3ish trunk: include-fixed required
    Roland uses a different method (see patch set below).
  4. ^  Patches from glibc-cvs mailing list: libc configure.in configure
  5. ^  From the libc-alpha mailing list: glibc-2.7 and autoconf 2.61 dependency in libc/nptl/sysdeps/configure

Stabilized Toolchain Dependency Tables

PowerPC Toolchain With DFP (Decimal Floating Point) Support
VeinGCCGLIBCBinutilsGDBGMPMPFR
Base VersionsIBM Branch 4.1.1 revision 127251Version 2.7 mainline cvs [2007-08-02]Version 2.17.50 [2007-06-28][6]Version 6.6[7]Version 4.2.1[8]satisfies Version 4.1.0+ requirement of MPFRVersion 2.3.0 [9], --enable-shared defaults to 'on' in Version 2.3.0
Stabilization RequiredPrivate_futex patch[10] ppc32 rpath patchsingle-step patch, altivec patch, long-double patch, loader-break patch
Secure PLTLibgcc plt-fix patch[11]sysdep-cancel.h patch[12]socket.S patch[13]
Power6 Enablement[in base 127251]powerpc-cpu support [in base 2007-08-02]BFD mtfsf fix [in base 2007-06-28]mtfsf BFD patch, power6-reg patch
DFP Enablement[in base 127251]libdfp v0.07, dfp<->int conversionsdfp insns [in base 2007-06-28]dfp insns patch
Fortran Support[in base 127251]GCC Requires GMP Version 4.1+GCC Requires MPFR Version 2.2.1+
  1. ^  Binutils 2.17.50 provides BFD support for an extended 'mtfsfi' instruction, among others, which take a optional third parameter as necessary on Power6 for setting the high-order 32-bits of the new 64-bit FPSCR register.
  2. ^  GDB Version 6.6
  3. ^  GMP Version 4.2.1
  4. ^  MPFR Version 2.3.0
  5. ^  GLIBC private futex Patch
  6. ^  Libgcc plt-fix patch
  7. ^  GLIBC sysdep-cancel.h patch
  8. ^  GLIBC socket.S patch
크리에이티브 커먼즈 라이센스
Creative Commons License
2012/02/10 10:32 2012/02/10 10:32
받은 트랙백이 없고, 댓글이 없습니다.

댓글+트랙백 RSS :: http://blog.hwport.com/rss/response/644

댓글+트랙백 ATOM :: http://blog.hwport.com/atom/response/644


본 내용은 http://blog.naver.com/semigifn?Redirect=Log&logNo=15050088 에서 발췌한 것임을 밝힙니다.



HARDWARE b2c2

From Gentoo Linux Wiki

This article is part of the Hardware series.
Laptops  TV Tuner Cards  Wireless  Servers
Storage  Other Hardware  Motherboards  Related

Please format this article according to the guidelines and Wikification suggestions, then remove this notice {{Wikify}} from the article

Installation

You must have the modules for the card compiled into your kernel as modules:

 Device Drivers  --->
    Multimedia devices  --->
      Digital Video Broadcasting Devices --->
        <M>   DVB Core Support
        ---   Supported Frontend Modules
        <M> STV0299 based DVB-S frontend (QPSK)
        <M> Zarlink MT312 Satellite Channel Decoder (QPSK)
        --- Supported FlexCopII (B2C2) Adapters
        <M> Technisat Skystar2 PCI

You can load the corespondig modules automaticaly by adding them to /etc/modules.autoload.d/kernel-2.6, if you do not want the modules to be loaded at boot, you can load them with the modprobe commando.

File: /etc/modules.autoload.d/kernel-2.6
mt312 
skystar2 
stv0299

Using the Card

I prefer using this card with the xine player. You must have dvb in your useflags defined, so that anemerge xine-lib xine-ui compiles the dvb-plugin for xine. After putting a channels.conf in ~/.xine you can start xine, with a click on dvb you should be able to watch tv now. To switch beetween the channels you can try to use your mousewheel, or deaktivate Numlock and use the keys 8 and 2!

File: /~/.xine/channels.conf
:-Astra 19,2 E--
Das Erste:11837:h:0:27500:101:102:0:0:28106
ZDF:11954:h:0:27500:110:120:0:0:28006
3sat:11954:h:0:27500:210:220:0:0:28007
EinsMuXx:12110:h:0:27500:301:302:0:0:28203
EinsExtra:12110:h:0:27500:101:102:0:0:28201
EinsFestival:12110:h:0:27500:201:202:0:0:28202
MDR FERNSEHEN:12110:h:0:27500:401:402:0:0:28204
RBB Brandenburg:12110:h:0:27500:501:502:0:0:28205
RBB Berlin:12110:h:0:27500:601:602:0:0:28206
SWR Fernsehen:11837:h:0:27500:801:802:0:0:28113
SR Fernsehen Suedwes:11837:h:0:27500:501:502:0:0:28110
SDWEST RP:12110:h:0:27500:3101:3102:3104:0:28231
hessen fernsehen:11837:h:0:27500:301:302:0:0:28108
WDR K ln:11837:h:0:27500:601:602:0:0:28111
Bayerisches FS:11837:h:0:27500:201:202:0:0:28107
NDR FS MV:12110:h:0:27500:2401:2402:0:0:28224
BR-alpha:11837:h:0:27500:701:702:0:0:28112
KiKa:11954:h:0:27500:310:320:330:0:28008
arte:11836:h:0:27500:401:402:404:0:28109
ZDF Theaterkanal:11954:h:0:27500:1110:1120:0:0:28016
ZDF.info:11954:h:0:27500:610:620:0:0:28011
ZDF.doku:11954:h:0:27500:660:670:0:0:28014
Phoenix:11837:h:0:27500:901:902:0:0:28114
DW-TV:11597:v:0:21997:1000:1001:0:0:10020
MDR THRINGEN:12110:h:0:27500:401:402:0:0:28230
MDR S-ANHALT:12110:h:0:27500:401:402:0:0:28229
MDR SACHSEN:12110:h:0:27500:401:402:0:0:28228
NDR FS SH:12110:h:0:27500:2401:2402:0:0:28227
NDR FS NDS:12110:h:0:27500:2401:2402:0:0:28226
NDR FS HH:12110:h:0:27500:2401:2402:0:0:28225
WDR D sseldorf:12422:h:0:27500:101:102:0:0:28308
WDR Essen:12422:h:0:27500:101:102:0:0:28309
WDR Dortmund:12422:h:0:27500:101:102:0:0:28307
WDR Bielefeld:12422:h:0:27500:101:102:0:0:28306
WDR Siegen:12422:h:0:27500:101:102:0:0:28311
WDR Wuppertal:12422:h:0:27500:101:102:0:0:28312
WDR M nster:12422:h:0:27500:1a01:102:0:0:28310
WDR Aachen:12422:h:0:27500:101:102:0:0:28305
:-- Free TV D --
RTL Television:12188:h:0:27500:163:104:0:0:12003
SAT.1:12480:v:0:27500:1791:1792:0:0:46
ProSieben:12480:v:0:27500:255:256:0:0:898
RTL2:12188:h:0:27500:166:128:68:0:12020
Super RTL:12188:h:0:27500:165:120:0:0:12040
KABEL1:12480:v:0:27500:511:512:0:0:899
VOX:12188:h:0:27500:167:136:71:0:12060
BTV:12226:h:0:27500:512:660:0:0:31210
TELE 5:12480:v:0:27500:1535:1536:38:0:51
TV.BERLIN:12149:h:0:27500:511:512:0:0:772
XXP:12631:h:0:21995:203:303:503:0:12602
ONTV Regional:12149:h:0:27500:2303:2304:0:0:514
Franken SAT:12149:h:0:27500:3583:3584:0:0:775
rhein main tv:12633:h:0:22000:208:308:508:0:12614
NEUN LIVE Televisi:12480:v:0:27500:767:768:0:0:897
Bloomberg TV Germany:12552:v:0:22000:162:99:0:0:12160
EuroNews:11954:h:0:27500:2221:2233:0:0:28015
EURONEWS:11817:v:0:27500:163:99:0:0:8004
N24:12480:v:0:27500:2047:2048:0:0:47
n-tv:12670:v:0:22000:162:96:55:0:12730
DSF:12480:v:0:27500:1023:1024:0:0:900
Eurosport:11954:h:0:27500:410:420:0:0:28009
Bahn TV:12631:h:0:21995:201:301:0:0:12600
TV6:12610:v:0:22000:100:101:4201:0:12301
RAZE TV:10832:h:0:22000:308:256:0:0:61963
HOERZU DIGITAL:10832:h:0:22000:5169:8191:0:0:61961
Bibel TV:10832:h:0:22000:32:33:0:0:61900
K-TV:12631:h:0:21995:202:302:0:0:12601
Home Order Tel:12480:v:0:27500:1279:1280:0:0:40
QVC GERMANY:12552:v:0:22000:165:166:0:0:12100
RTL Shop:12188:h:0:27500:168:137:70:0:12080
TV TRAVEL SHOP:12149:h:0:27500:1023:1024:37:0:769
lastminute.de:12149:h:0:27500:3071:3072:0:0:54
Sonnenklar TV:12480:v:0:27500:2303:2304:0:0:32
K1010:12631:h:0:21995:1041:1042:0:0:12616
Spielekanal TV:12631:h:0:21995:1053:1054:1055:0:12618
OTTO SHOP:12460:h:0:27500:255:8191:0:0:901
VIC-TV.NET DE1:12460:h:0:27500:511:8191:0:0:60
VIC-TV.NET DE2:12460:h:0:27500:767:8191:0:0:61
TW 1:12692:h:0:22000:166:167:0:0:13013
SAT.1 A:12051:v:0:27500:800:801:802:0:20005
SAT.1-CH:12149:h:0:27500:255:256:34:0:48
GOD Channel:12149:h:0:27500:767:768:0:0:774
RNFplus:12149:h:0:27500:1104:1105:0:0:768
ProSieben A:12051:v:0:27500:161:84:0:0:20002
ProSieben CH:12051:v:0:27500:289:290:0:0:20001
Kabel 1 Austria:12051:v:0:27500:166:167:0:0:20004
Kabel 1 Schweiz:12051:v:0:27500:162:163:0:0:20003
RTL Austria:12226:h:0:27500:201:202:203:0:28800
VOX Austria:12226:h:0:27500:301:302:303:0:28805
RTL2 Austria:12226:h:0:27500:401:402:403:0:28810
Super RTL A:12226:h:0:27500:501:502:503:0:28815
:-- Free TV INT --
CNN Int.:11778:v:0:27500:165:100:0:0:28522
Al Jazeera:11568:v:0:22000:55:56:0:0:9021
Sky News:11597:v:0:22000:305:306:0:0:28707
BBC WORLD:12285:v:0:27500:167:108:0:0:17027
2M Maroc:11568:v:0:22000:139:140:0:0:9030
TV TWRAM:10832:h:0:22000:70:71:0:0:61911
Free-X TV:10832:h:0:22000:160:80:0:0:61980
Ocko TV:10832:h:0:22000:161:162:0:0:61990
CANAL CANARIAS:10979:v:0:22000:164:96:0:0:30661
CCVALENCIANA:11436:v:0:22000:169:116:0:0:29912
OKplus:11509:v:0:22000:100:8191:0:0:30100
CANAL ALGERIE:11568:v:0:22000:168:138:0:0:9011
TV7:11568:v:0:22000:166:128:0:0:9018
TV 5:11568:v:0:22000:164:112:0:0:9012
ESC1 - EGYPT:11568:v:0:22000:163:104:0:0:9014
RAI 1:11568:v:0:22000:289:290:0:0:9015
RTPI:11568:v:0:22000:161:301:0:0:9017
ARTE:11568:v:0:22000:167:136:0:0:9019
Eurosport News:11597:v:0:22000:1000:1001:0:0:10011
TVEi:11597:v:0:22000:50:51:0:0:10001
RTM MAROC:11597:v:0:22000:63:62:0:0:10002
ETB SAT:11686:v:0:22000:163:92:0:0:30203
TVC INT.:11686:v:0:22000:161:84:35:0:30201
ANDALUCA TV:11686:v:0:22000:162:88:38:0:30202
TV GALICIA:11686:v:0:22000:167:108:53:0:30222
TM SAT/LA OTRA:11686:v:0:22000:164:96:44:0:30204
Travel:11778:v:0:27500:163:92:0:0:28003
hollywood cinema:12460:h:0:27500:1023:1024:0:0:663
LA CINQUIEME:12207:v:0:27500:160:80:0:0:8501
LCP:12207:v:0:27500:165:100:0:0:8506
BEUR TV:12324:v:0:27500:167:108:0:0:8613
Chamber TV:12552:v:0:22000:55:56:0:0:12180
RTL TELE Letzebuerg:12552:v:0:22000:168:144:74:0:3994
Club Teleachat:12611:v:0:22000:53:54:0:0:12270
RTBF SAT:12610:v:0:22000:48:49:0:0:3982
CNBC Europe:12611:v:0:22000:944:945:0:0:12200
LibertyTV.com:12611:v:0:22000:941:942:0:0:12280
TV5 Europe:12611:v:0:22000:45:46:0:0:12240
Motors TV:12611:v:0:22000:191:194:0:0:12300

[출처] HARDWARE b2c2|작성자 새미기픈

크리에이티브 커먼즈 라이센스
Creative Commons License
2012/02/08 16:37 2012/02/08 16:37
받은 트랙백이 없고, 댓글이 없습니다.

댓글+트랙백 RSS :: http://blog.hwport.com/rss/response/643

댓글+트랙백 ATOM :: http://blog.hwport.com/atom/response/643




본 내용은 "http://xeraph.com/3788336" 에서 발췌한 것임을 밝힙니다.

또한 알고리즘에 대한 원문은 아래의 파일을 참조하세요.
 

----

문자열 A는 ABCABBA 라고 하고, 문자열 B가 CBABAC라고 하자. 문자열 A, B를 각각 X축, Y축으로 한 글자씩 배열하여 아래와 같 모양으로 벡터 그래프를 만들어낼 수 있다. 종류별로 수평 벡터, 수직 벡터, 대각선 벡터가 있는데, 수평 벡터와 수직 벡터는 그냥 단방향으로 그어놓은 것이고, 대각선 벡터는 문자가 서로 같은 점을 끝점으로 해서 만든다. 즉, 아래 그림에서 (3, 1)이나 (5, 2) 처럼 문자가 같으면 그 위치에 대각선이 그어지는 것이다.

사용자 삽입 이미지

이 그림에 경로가 하나 표시되어 있다. 경로 하나를 따라가면 공통 문자열이나 편집 명령을 얻어낼 수 있다. 공통 문자열은 대각선 벡터의 끝점이 표시하는 문자다. 즉 저 경로에서 대각선 벡터 끝점만 뽑아서 나열하면 CABA가 된다. 편집 명령은 수평/수직 벡터를 따라가면서 만든다. 오른쪽으로 가면 삭제 (D), 아래쪽으로 가면 입력 (I) 으로 해석할 수 있다. 위 경로를 따라가면 1D (첫번째 문자 삭제), 2D (두번째 문자 삭제), 3IB (세번째 문자 뒤에 B를 입력), 6D (6번째 문자 삭제), 7IC (7번째 문자 뒤에 C를 입력)이 나온다.

문자열 A 원본 편집 시작 : ABCABBA
A, B 삭제 : CABBA
B 입력 : CBABBA
B 삭제 : CBABA
C 입력 : CBABAC
문자열 B 완성됨 : CBABAC

여기에서 알 수 있는 것은 같은 경로에서 대각선 벡터 따면 공통 문자열이 나오고 수평/수직 벡터를 따면 편집 명령이 나오니 LCS와 SES 문제는 Dual 관계라는 것이다. (Dual을 뭐라고 번역하더라) LCS(longest common sequence)는 주어진 문자열 2개의 가장 기다란 공통 문자열을 찾아내는 문제이고, SES(shortest edit path)는 문자열 A를 문자열 B로 변환할 때 최소한의 편집 명령 (edit script) 을 찾아내는 문제이다. 그리고 SES 문제는 (0, 0)에서 (N, M)까지 만들어진 벡터 그래프에서 최소한의 대각선 아닌 벡터(즉, 수평 벡터와 수직 벡터)를 포함하는 경로를 찾는 것과 동등하다. 이 벡터 그래프에 가중치를 준다고 하자. 대각선은 0, 수평/수직 벡터는 1로 주는 것이다. 이렇게 하면 LCS/SES 문제는 (0,0)에서 (N, M)에 이르는 가장 짧은 경로를 찾는 문제의 형태로 바뀐다. 오~

그럼 이제 직관적으로 생각할 수 있는 것은 그리디 알고리즘이다. 
크리에이티브 커먼즈 라이센스
Creative Commons License
2012/01/09 17:22 2012/01/09 17:22
받은 트랙백이 없고, 댓글이 없습니다.

댓글+트랙백 RSS :: http://blog.hwport.com/rss/response/641

댓글+트랙백 ATOM :: http://blog.hwport.com/atom/response/641



본 내용은 "http://blog.daum.net/gusami/16089659" 에서 발췌한 것임을 밝힙니다.


Zero Copy I: User-Mode Perspective

January 1st, 2003 by Dragan Stancevic in

Explaining what is zero-copy functionality for Linux, why it's useful and where it needs work.
6digg 

By now almost everyone has heard of so-called zero-copy functionality under Linux, but I often run into people who don't have a full understanding of the subject. Because of this, I decided to write a few articles that dig into the matter a bit deeper, in the hope of unraveling this useful feature. In this article, we take a look at zero copy from a user-mode application point of view, so gory kernel-level details are omitted intentionally.

What Is Zero-Copy?

To better understand the solution to a problem, we first need to understand the problem itself. Let's look at what is involved in the simple procedure of a network server dæmon serving data stored in a file to a client over the network. Here's some sample code:

read(file, tmp_buf, len);
write(socket, tmp_buf, len);

Looks simple enough; you would think there is not much overhead with only those two system calls. In reality, this couldn't be further from the truth. Behind those two calls, the data has been copied at least four times, and almost as many user/kernel context switches have been performed. (Actually this process is much more complicated, but I wanted to keep it simple). To get a better idea of the process involved, take a look at Figure 1. The top side shows context switches, and the bottom side shows copy operations.

Figure 1. Copying in Two Sample System Calls

Step one: the read system call causes a context switch from user mode to kernel mode. The first copy is performed by the DMA engine, which reads file contents from the disk and stores them into a kernel address space buffer.

Step two: data is copied from the kernel buffer into the user buffer, and the read system call returns. The return from the call caused a context switch from kernel back to user mode. Now the data is stored in the user address space buffer, and it can begin its way down again.

Step three: the write system call causes a context switch from user mode to kernel mode. A third copy is performed to put the data into a kernel address space buffer again. This time, though, the data is put into a different buffer, a buffer that is associated with sockets specifically.

Step four: the write system call returns, creating our fourth context switch. Independently and asynchronously, a fourth copy happens as the DMA engine passes the data from the kernel buffer to the protocol engine. You are probably asking yourself, “What do you mean independently and asynchronously? Wasn't the data transmitted before the call returned?” Call return, in fact, doesn't guarantee transmission; it doesn't even guarantee the start of the transmission. It simply means the Ethernet driver had free descriptors in its queue and has accepted our data for transmission. There could be numerous packets queued before ours. Unless the driver/hardware implements priority rings or queues, data is transmitted on a first-in-first-out basis. (The forked DMA copy in Figure 1 illustrates the fact that the last copy can be delayed).

As you can see, a lot of data duplication is not really necessary to hold things up. Some of the duplication could be eliminated to decrease overhead and increase performance. As a driver developer, I work with hardware that has some pretty advanced features. Some hardware can bypass the main memory altogether and transmit data directly to another device. This feature eliminates a copy in the system memory and is a nice thing to have, but not all hardware supports it. There is also the issue of the data from the disk having to be repackaged for the network, which introduces some complications. To eliminate overhead, we could start by eliminating some of the copying between the kernel and user buffers.

One way to eliminate a copy is to skip calling read and instead call mmap. For example:

tmp_buf = mmap(file, len);
write(socket, tmp_buf, len);

To get a better idea of the process involved, take a look at Figure 2. Context switches remain the same.

Figure 2. Calling mmap

Step one: the mmap system call causes the file contents to be copied into a kernel buffer by the DMA engine. The buffer is shared then with the user process, without any copy being performed between the kernel and user memory spaces.

Step two: the write system call causes the kernel to copy the data from the original kernel buffers into the kernel buffers associated with sockets.

Step three: the third copy happens as the DMA engine passes the data from the kernel socket buffers to the protocol engine.

By using mmap instead of read, we've cut in half the amount of data the kernel has to copy. This yields reasonably good results when a lot of data is being transmitted. However, this improvement doesn't come without a price; there are hidden pitfalls when using the mmap+write method. You will fall into one of them when you memory map a file and then call write while another process truncates the same file. Your write system call will be interrupted by the bus error signal SIGBUS, because you performed a bad memory access. The default behavior for that signal is to kill the process and dump core—not the most desirable operation for a network server. There are two ways to get around this problem.

The first way is to install a signal handler for the SIGBUS signal, and then simply call return in the handler. By doing this the write system call returns with the number of bytes it wrote before it got interrupted and the errno set to success. Let me point out that this would be a bad solution, one that treats the symptoms and not the cause of the problem. Because SIGBUS signals that something has gone seriously wrong with the process, I would discourage using this as a solution.

The second solution involves file leasing (which is called “opportunistic locking” in Microsoft Windows) from the kernel. This is the correct way to fix this problem. By using leasing on the file descriptor, you take a lease with the kernel on a particular file. You then can request a read/write lease from the kernel. When another process tries to truncate the file you are transmitting, the kernel sends you a real-time signal, the RT_SIGNAL_LEASE signal. It tells you the kernel is breaking your write or read lease on that file. Your write call is interrupted before your program accesses an invalid address and gets killed by the SIGBUS signal. The return value of the write call is the number of bytes written before the interruption, and the errno will be set to success. Here is some sample code that shows how to get a lease from the kernel:

if(fcntl(fd, F_SETSIG, RT_SIGNAL_LEASE) == -1) {
    perror("kernel lease set signal");
    return -1;
}
/* l_type can be F_RDLCK F_WRLCK */
if(fcntl(fd, F_SETLEASE, l_type)){
    perror("kernel lease set type");
    return -1;
}

You should get your lease before mmaping the file, and break your lease after you are done. This is achieved by calling fcntl F_SETLEASE with the lease type of F_UNLCK.

Sendfile

In kernel version 2.1, the sendfile system call was introduced to simplify the transmission of data over the network and between two local files. Introduction of sendfile not only reduces data copying, it also reduces context switches. Use it like this:

sendfile(socket, file, len);

To get a better idea of the process involved, take a look at Figure 3.

Figure 3. Replacing Read and Write with Sendfile

Step one: the sendfile system call causes the file contents to be copied into a kernel buffer by the DMA engine. Then the data is copied by the kernel into the kernel buffer associated with sockets.

Step two: the third copy happens as the DMA engine passes the data from the kernel socket buffers to the protocol engine.

You are probably wondering what happens if another process truncates the file we are transmitting with the sendfile system call. If we don't register any signal handlers, the sendfile call simply returns with the number of bytes it transferred before it got interrupted, and the errno will be set to success.

If we get a lease from the kernel on the file before we call sendfile, however, the behavior and the return status are exactly the same. We also get the RT_SIGNAL_LEASE signal before the sendfile call returns.

So far, we have been able to avoid having the kernel make several copies, but we are still left with one copy. Can that be avoided too? Absolutely, with a little help from the hardware. To eliminate all the data duplication done by the kernel, we need a network interface that supports gather operations. This simply means that data awaiting transmission doesn't need to be in consecutive memory; it can be scattered through various memory locations. In kernel version 2.4, the socket buffer descriptor was modified to accommodate those requirements—what is known as zero copy under Linux. This approach not only reduces multiple context switches, it also eliminates data duplication done by the processor. For user-level applications nothing has changed, so the code still looks like this:

sendfile(socket, file, len);

To get a better idea of the process involved, take a look at Figure 4.

Figure 4. Hardware that supports gather can assemble data from multiple memory locations, eliminating another copy.

Step one: the sendfile system call causes the file contents to be copied into a kernel buffer by the DMA engine.

Step two: no data is copied into the socket buffer. Instead, only descriptors with information about the whereabouts and length of the data are appended to the socket buffer. The DMA engine passes data directly from the kernel buffer to the protocol engine, thus eliminating the remaining final copy.

Because data still is actually copied from the disk to the memory and from the memory to the wire, some might argue this is not a true zero copy. This is zero copy from the operating system standpoint, though, because the data is not duplicated between kernel buffers. When using zero copy, other performance benefits can be had besides copy avoidance, such as fewer context switches, less CPU data cache pollution and no CPU checksum calculations.

Now that we know what zero copy is, let's put theory into practice and write some code. You can download the full source code from www.xalien.org/articles/source/sfl-src.tgz. To unpack the source code, type tar -zxvf sfl-src.tgz at the prompt. To compile the code and create the random data file data.bin, run make.

Looking at the code starting with header files:

/* sfl.c sendfile example program
Dragan Stancevic <
header name                 function / variable
-------------------------------------------------*/
#include <stdio.h>          /* printf, perror */
#include <fcntl.h>          /* open */
#include <unistd.h>         /* close */
#include <errno.h>          /* errno */
#include <string.h>         /* memset */
#include <sys/socket.h>     /* socket */
#include <netinet/in.h>     /* sockaddr_in */
#include <sys/sendfile.h>   /* sendfile */
#include <arpa/inet.h>      /* inet_addr */
#define BUFF_SIZE (10*1024) /* size of the tmp
                               buffer */

Besides the regular <sys/socket.h> and <netinet/in.h> required for basic socket operation, we need a prototype definition of the sendfile system call. This can be found in the <sys/sendfile.h> server flag:

/* are we sending or receiving */
if(argv[1][0] == 's') is_server++;
/* open descriptors */
sd = socket(PF_INET, SOCK_STREAM, 0);
if(is_server) fd = open("data.bin", O_RDONLY);
The same program can act as either a server/sender or a client/receiver. We have to check one of the command-prompt parameters, and then set the flag is_server to run in sender mode. We also open a stream socket of the INET protocol family. As part of running in server mode we need some type of data to transmit to a client, so we open our data file. We are using the system call sendfile to transmit data, so we don't have to read the actual contents of the file and store it in our program memory buffer. Here's the server address:
/* clear the memory */
memset(&sa, 0, sizeof(struct sockaddr_in));
/* initialize structure */
sa.sin_family = PF_INET;
sa.sin_port = htons(1033);
sa.sin_addr.s_addr = inet_addr(argv[2]);
We clear the server address structure and assign the protocol family, port and IP address of the server. The address of the server is passed as a command-line parameter. The port number is hard coded to unassigned port 1033. This port number was chosen because it is above the port range requiring root access to the system.

Here is the server execution branch:

if(is_server){
    int client; /* new client socket */
    printf("Server binding to [%s]\n", argv[2]);
    if(bind(sd, (struct sockaddr *)&sa,
                      sizeof(sa)) < 0){
        perror("bind");
        exit(errno);
    }

As a server, we need to assign an address to our socket descriptor. This is achieved by the system call bind, which assigns the socket descriptor (sd) a server address (sa):

if(listen(sd,1) < 0){
    perror("listen");
    exit(errno);
}
Because we are using a stream socket, we have to advertise our willingness to accept incoming connections and set the connection queue size. I've set the backlog queue to 1, but it is common to set the backlog a bit higher for established connections waiting to be accepted. In older versions of the kernel, the backlog queue was used to prevent syn flood attacks. Because the system call listen changed to set parameters for only established connections, the backlog queue feature has been deprecated for this call. The kernel parameter tcp_max_syn_backlog has taken over the role of protecting the system from syn flood attacks:
if((client = accept(sd, NULL, NULL)) < 0){
    perror("accept");
    exit(errno);
}
The system call accept creates a new connected socket from the first connection request on the pending connections queue. The return value from the call is a descriptor for a newly created connection; the socket is now ready for read, write or poll/select system calls:
if((cnt = sendfile(client,fd,&off,
                          BUFF_SIZE)) < 0){
    perror("sendfile");
    exit(errno);
}
printf("Server sent %d bytes.\n", cnt);
close(client);
A connection is established on the client socket descriptor, so we can start transmitting data to the remote system. We do this by calling the sendfile system call, which is prototyped under Linux in the following manner:
extern ssize_t
sendfile (int __out_fd, int __in_fd, off_t *offset,
          size_t __count) __THROW;
The first two parameters are file descriptors. The third parameter points to an offset from which sendfile should start sending data. The fourth parameter is the number of bytes we want to transmit. In order for the sendfile transmit to use zero-copy functionality, you need memory gather operation support from your networking card. You also need checksum capabilities for protocols that implement checksums, such as TCP or UDP. If your NIC is outdated and doesn't support those features, you still can use sendfile to transmit files. The difference is the kernel will merge the buffers before transmitting them.
Portability Issues

One of the problems with the sendfile system call, in general, is the lack of a standard implementation, as there is for the open system call. Sendfile implementations in Linux, Solaris or HP-UX are quite different. This poses a problem for developers who wish to use zero copy in their network data transmission code.

One of the implementation differences is Linux provides a sendfile that defines an interface for transmitting data between two file descriptors (file-to-file) and (file-to-socket). HP-UX and Solaris, on the other hand, can be used only for file-to-socket submissions.

The second difference is Linux doesn't implement vectored transfers. Solaris sendfile and HP-UX sendfile have extra parameters that eliminate overhead associated with prepending headers to the data being transmitted.

Looking Ahead

The implementation of zero copy under Linux is far from finished and is likely to change in the near future. More functionality should be added. For example, the sendfile call doesn't support vectored transfers, and servers such as Samba and Apache have to use multiple sendfile calls with the TCP_CORK flag set. This flag tells the system more data is coming through in the next sendfile calls. TCP_CORK also is incompatible with TCP_NODELAY and is used when we want to prepend or append headers to the data. This is a perfect example of where a vectored call would eliminate the need for multiple sendfile calls and delays mandated by the current implementation.

One rather unpleasant limitation in the current sendfile is it cannot be used when transferring files greater than 2GB. Files of such size are not all that uncommon today, and it's rather disappointing having to duplicate all that data on its way out. Because both sendfile and mmap methods are unusable in this case, a sendfile64 would be really handy in a future kernel version.

Conclusion

Despite some drawbacks, zero-copy sendfile is a useful feature, and I hope you have found this article informative enough to start using it in your programs. If you have a more in-depth interest in the subject, keep an eye out for my second article, titled “Zero Copy II: Kernel Perspective”, where I will dig a bit more into the kernel internals of zero copy.

크리에이티브 커먼즈 라이센스
Creative Commons License
2011/12/27 17:48 2011/12/27 17:48
받은 트랙백이 없고, 댓글이 없습니다.

댓글+트랙백 RSS :: http://blog.hwport.com/rss/response/640

댓글+트랙백 ATOM :: http://blog.hwport.com/atom/response/640




본 내용은 "http://www.funit.net/git" 에서 발췌한 것임을 밝힙니다.




git 튜토리얼 메뉴얼 페이지

개요

이 튜토리얼은 프로젝트를 임포트하고, 수정하고, 수정한 결과를 다른 개발자들과 공유하는 방법을 설명한다.

최신 버젼의 소스를 테스트하기 위해서 라든지 git으로 프로젝트를 패치해 오는 것에만 관심이 있는 경우, Git User's Manual의 첫 두 장을 읽어 보는 편이 좋을 것이다.

먼저, 명령어에 대한 문서를 보려면 man또는 git help를 사용한다. 예를 들어 git log --graph라는 명령의 도움말을 보려면:

  $man git-log

또는:

  $ git help log


라고 하면 된다.

git help를 사용할 경우에는 자신이 지정한 메뉴얼 뷰어로 보는 것이 가능하다. 자세한 내용은 get-help 메뉴얼을 참고한다.

git 으로 작업을 하기 전에 자신에 대한 정보(이름과 email)를 미리 설정에 두는 것이 좋다. 아래와 같이 하면 된다.

$git config --global user.name "여기에 이름을..."
$git config --global user.email you@yourdomain.example.com

새 프로젝트 임포트하기


처음으로 작성한 project.tar.gz라는 프로젝트 압축파일이 있다고 하자. 아래와 같이 git 버젼관리로 임포트해서 버젼 관리를 시작할 수 있다.

$tar xzf project.tar.gz
$cd project
$git init


압축을 풀고 그 디렉토로로 가서 git을 초기화하였다.
아래와 같이 결과가 출력될 것이다.

Initialized empty Git repository in .git/


(.git/ 디렉토리에 빈 Git 리파지토리가 초기화 되었습니다)
즉, 작업 디렉토리를 초기화 한 것이다. ".git"라는 디렉토리가 생성된 것을 볼 수 있을 것이다.
다음은 git-add를 사용해서 현재 디렉토리(.)의 모든 내용을 스넵샷으로 저장한다.

$git add .


현재의 스넵샷이  "index"라고 하는 임시 스테이지 영역에 저장되었다. 이제 git-commit을 이용해서 index의 내용을 리파지토리에 영구 저장할 수 있다.

$git commit


이를 실행하면 커밋 메세지 입력을 요구할 것이다. 이렇게 프로젝트의 첫 벗째 버젼을 git에 저장하였다.

변경을 가하기


몇몇 파일을 수정하고, index에 저장하려면 아래와 같이 한다.

$git add file1 file2 file3


이제 커밋을 할 수 있는 상태가 되었다. 이 때 git-diff명령에 --cached 옵션을 사용해서 commit할 내용의 미리보기를 해 보자.
 
$git diff -cached


(--cached 옵션을 지정하지 않으면, 현재 디렉토리와 index사이의 차이점이 표시된다.)

git-status 명령을 사용해서 현재상태를 확인할 수 있다.

$git status
# On branch master
# Changes to be committed:
#   (user "get reset HEAD <file>..." to unstage)
#
#        modified:   file1
#        modified:   file2
#        modified:   file3
#


더 수정할 것이 있다면 수정을 하고, 그 내용을 index에 적용한 다음에 마지막으로 커밋을 한다.

$git commit


마찬가지로 커밋에 대한 간단한 설명을 입력하는 프롬프트가 표시될 것이다.
git-add 를 생략할 수 있는 방법도 있다.

$git commit -a


를 실행하면 수정된 파일들을 자동으로 인식해서 index에 넣고 커밋까지 한번에 해준다. 단 새로 생성된 파일은 자동으로 add되지 않는다.

커밋 메세지에 대해서: 필수로 입력해야 하는 것은 아니지만, 첫 번째 줄에 수정 내용에 대한 전체적인 요약을 50문자 이내로 기술하고 한 줄을 띄우고 상세한 내용을 기술하는 것이 좋다. 예를 들어, 커밋의 내용을 email로 보내 주는 툴들은 첫 번째 라인을 제목으로 사용하고 나머지를 본문으로 사용한다.

Git은 파일자체를 트렉킹하는 것이 아니고 내용을 트렉킹한다.


많은 버젼관리 시스템들은 새로운 파일을 버젼관리에 추가하기 위해 add 명령을 사용한다. 반면에 git의 add명령은 더 심플하면서 좀 더 강한 기능을 가진다. git-add는 새로운 파일의 추가에도 사용하지만 이미 있는 파일들중에서 수정된 파일의 내용을 index에 저장할 때도 사용된다 (영어로 스냅샷을 스테이징한다라고 표현). commit을 하면 이 index에 저장된 스냅샷이 commit되게 된다.

프로젝트 히스토리 보기

변경 내용의 히스토리를 열람하려면

$git log


명령을 사용한다. 모든 스텝별 변경사항을 보려면

$git log -p


를 실행한다.

때로는 요약한 내용으로 보는 것이 편할 때가 있다. 이럴 때 아래와 같이 실행한다.

$git log --stat --summary

브랜치 관리하기


하나의 git 리파지포리에 복수 개의 브랜치를 유지할 수 있다. 새로운 브랜치 "experimental"을 생성하려면

$git branch experimental


이라고 실행한다. 이제

$git branch


라고 실행하면 아래와 같이 브란치의 리스트를 표시할 것이다.

  experimental
* master


"experimental" 브랜치가 방금 생성한 브랜치이다. "master" 브랜치는 처음에 자동으로 생성되는 기본 브란치이다. * 마크는 현재 사용중인 브랜치를 가리킨다.

$git checkout experiment


라고 실행해서 experimental 브랜치로 스위치할 수 있다. 파일을 수정해서 커밋을 한 다음 master 브랜치로 되돌아가 보자.

(파일 수정)
$git commit -a
$git checkout master


방금 수정했던 내용들이 다 사라졌을 것이다. 왜냐하면 experimental 브랜치에 수정을 커밋한 다음에 master 브랜치로 스위치 해서 돌아 왔기 때문이다.
master에도 수정을 가해보자.

(파일 수정)
$git commit -a


이제 두개의 브랜치는 각각 서로 다른 수정분들을 가지고 있다. experimental에 가했던 수정을 master로 병합하려면 아래와 같이 실행한다.

$git merge experimental


각 수정본들이 충돌하지 않았다면 이제 커밋을 할 수 있는 상태가 된다(병합 결과를 master에 커밋할 수 있는 상태). 만약 충돌하는 부분이 있을 경우, 해당 파일내에 충돌내용을 표기하는 마크가 남을 것이다.

$git diff


명령으로 그 마크를 확인해 볼 수 있다. 충돌한 부분을 해결했다면 아래와 같이

$git commit -a


실행해서 병합 + 충돌 해결분을 커밋해서 병합을 완료한다. 마지막으로

$gitk


를 실행하면 히스토리를 멋있는 그래픽으로 표시할 것이다.
이제 experimental은 삭제해도 된다.

$git branch -d experimental


아직 병합이 완료가 되지 않은 경우에는 삭제할 수 없다. 만약 강제로 삭제하고 싶다면 아래의 예와 같이 -D 옵션을 사용한다.

$git branch -D crazy-idea


브랜치는 부하도 적고 사용하기도 쉽기 때문에 무언가를 실험해 보고 싶은 경우 좋은 방법이 된다.

git으로 협업하기


Alice가 /home/alice/project 에 git 리파지토리가 있는 상태로 새로운 프로젝트를 시작했고, 그리고 같은 머신에 홈디렉토리를 가지는 Bob이 도움을 주고 싶어한다고 하자.

Bob은

bob$ git clone /home/alice/project myrepo


라는 명령으로 myrepo라는 새로운 디렉토리에 Alice의 리파지토리를 복제해 온다. clone은 원래의 프로젝트의 히스토리까지 모두 자신의 것으로 복제해 온다.
Bob은 이제 파일들을 수정하고 커밋한다.

(파일 수정)
bob$git commit -a
(필요에 따라서 반복)


작업이 끝난 때에 Bob은 Allice에게 /home/bob/myrepo로 부터 수정내용을 가지고 가라고 한다. Alice는 아래와 같이 실행한다.

alice$cd /home/alice/project
alice$git pull /home/bob/myrepo master


이 명령은 Bob의 master 브랜치를 Alice의 현재 브랜치에 병합한다. 이 사이에 Alice가 내용을 수정해서 충돌이 발생했다면 충돌한 부분들을 직접 고쳐야할 것이다.

pull 명령은 2가지의 일을 한다. 원격의 브랜치로 부터 변경된 내용을 가지고 오기(fetch)와 현재 브랜치에 병합하기(merge)이다.

일반적으로 Alice는 pull을 하기 전에 현재의 변경내용을 커밋을 할 것이다. 만약 Bob의 작업내용이 Alice의 작업과 충돌한 경우 Alice는 현재의 작업디렉토리와 index를 사용해서 충돌을 해결하려고 할 것이지만 커밋하기 전의 변경내용들과 섞여버려서 방해가 될 것이기 때문이다. (실제로 git은 내용은 fetch해 오지만 merge는 실패하게 된다. 이런 경우 Alice는 어떤 방법으로라도 작업 디렉토리에 가했던 변경내용을 없애고 다시 pull을 실행해야 할 것이다.)  

Alice는 fetch 명령을 사용해서 실제로 Bob의 작업 내용을 merge하기 전에 확인해 볼 수 있다. 이를 위해  FETCH_HEAD라는 심볼을 사용한다.

alice$ git fetch /home/bob/myrepo master
alice$ git log -p HEAD..FETCH_HEAD


fetch 명령은 로컬에 아직 commit되지 않은 내용이 있더라도 안전하다. HEAD..FETCH_HEAD 범위 지정 표기는 FETCH_HEAD까지의 경위에서 HEAD까지의 경위을 뺀 범위를 의미한다. alice는 이미 HEAD까지의 경위를 알고 있고 Bob이 FETCH_HEAD의 상태가 되기까지의 경위(즉 아직 Alice의 시야에 없던 변화들)를 살펴보고 있는 것이다.

Alice는 Bob이 가한 변경을 시각적으로 확인해 보려면 아래와 같이 실행할 것이다.

alice$ gitk HEAD..FETCH_HEAD


git log에서 보았던 ..(점 둘) 범위 지정 표기를 사용하고 있다.
Alice는 Bob의 변경 내용 뿐만 아니라 자신의 변경 내용까지 한 번에 확인하고 싶을 때도 있을 것이다. 이럴 때 ..(점 둘)대신에 ...(점 셋)을 사용한다.

alice$ gitk HEAD...FETCH_HEAD


여기서 정의한 범위는 "각각의 경위를 모두 표시하되, 공통 경위는 제외하라"이다.
범위 지정 표기는 git log와 gitk 양쪽 모두에 사용할 수 있다는 점도 잊지 말자.

Bob이 변경한 내용을 살펴본 후 Alice는 급하게 pull하지 않아도 되겠다고 판단했다면 그냥 작업을 계속 진행한다. Bob이 변경한 내용에 지금 바로 필요한 내용이 있다면 현재 작업중인 내용을 잠시 피신(git-stash) 시키고 pull을 한 다음에 다시 작업하던 내용을 복귀(git-unstash) 시켜서 최신의 상태가 되도록 할 것이다.

작고 잘 짜여진 그룹에서 작업중이라면 특정 repository들과 반복해서 상호작용을 하게 될 것이다. 이런 repository를 간편하게 지정할 수 있도록 remote repository로 별명을 지을 수 있다.

alice$ git remote add bob /home/bob/myrepo


이제, Alice는 pull의 첫 부분인 git-fetch 명령을 아래와 같이 실행할 수 있다.

alice$git fetch bob


길게 적어서 fetch해 왔던 경우와는 다르게,  git-remote 명령으로 정했던 별명으로를 fetch를 실행한 경우, fetch해 온 내용은 remote 트랙킹 브랜치에 저장된다. 이 경우에는 bob/master 가 된다. 아래와 같이 실행하면

alice$git log -p master..bob/master


Alice의 master 브랜치로 부터 브랜치해 간 이래 변경된 내용을 보여주게 된다.
내용을 확인 했다면 Alice는 아래와 같은 명령으로 merge를 실행할 것이다.

alice$git merge bob/master


merge 명령 대신에 Alice의 remote 트랙킹 브랜치로 부터 pull해오는 방법도 있다.

alice$git pull . remotes/bob/master


git pull은 항상 현재의 브랜치로 merge하게 된다.
나중에, Bob은 아래의 명령으로 Alice의 변경내용을 pull해 온다.

bob$git pull


Bob은 굳이 Alice의 repository를 명시하지 않아도 됨에 주의한다. Bob이 Alice의 repository를 clone했을 때 git은 그녀의 repository 위치를 repository 설정에 저장해 두었기 때문이다.

bob$git config --get remote.origin.url
/home/alice/project


git-clone에 의해서 생성된 설정 내용은 git config -l 명령으로 확인 가능하다. git-config man 페이지를 참조하면 각 옵션들의 의미를 알아 볼 수 있다.

git은 당시의 Alice의 master 브랜치를 origin/master라는 이름으로 저장해 둔다.

bob$ git branch -r
origine/master


이후, Bob이 다른 호스트에서 작업하게 되었다면 ssh를 통해서 여전히 clone이나 pull이 가능하다.

bob$ git clone alice.org:/home/alice/project myrepo


이외에도 git용 프로토콜이나 rsync, http 등도 사용할 수 있다. 자세한 내용은 git-pull을 참고한다.
git은 CVS유사모드로도 사용할 수도 있다. 이 모드는 중앙 repository를 두고 여러 유저가 변경을 push한다. 자세한 내용은 git-push나 gitcvs-migration을 참고한다.

내력조회하기


git 내력이란 밀접한 commit들의 연속들을 의미한다. 앞에서 이미 git-log를 명령으로 commit들의 일람을 확인해 보았었다. 각 엔트리의 첫번째 라인은 커밋의 이름임을 주목하자.

$git log
commit c82a22c39cbc32576f64f5c6b3f24b99ea8149c7
Author: Junio C Hamano <junkio@cox.net>
Date:   Tue May 16 17:18:22 2006 -0700

merge-base: Clarify the comments on post processing


이 이름으로 git-show명령을 사용해서 해당 commit의 상세를 볼 수 있다.

$git show c82a22c39cbc32576f64f5c6b3f24b99ea8149c7


commit을 지정하는 방법은 이름말고도 더 있다. 이름의 앞 부분만 지정해도 (중복되지 않을 만큼 긴) 된다.

$git show c82a22c39c     #이름의 앞부분의 어느정도만 적어도 된다.
$git show HEAD                #현재 브랜치의 마지막
$git show experimental    #experimental 브랜치


모든 commit은 일반적으로 하나의 parent commit을 가진다. parent commit이란 프로젝트의 바로 앞의 상태를 가리킨다.

$git show HEAD^      #HEAD의 parent
$git show HEAD^^    #HEAD의 parent의 parent
$git show HEAD~4   #HEAD로 부터 4단계위의 parent


merge commit의 경우에는 하나 이상의 parent를 가질 수도 있다.

$git show HEAD^1     #HEAD의 첫번째 상위 parent(HEAD^와 동일)
$git show HEAD^2     #HEAD의 두번째 상위 parent


commit명에 별칭을 붙이는 것도 가능하다. 아래와 같이 실행하면

$git tag v2.5 1b2e1d63ff


1b2e1d63ff 대신에 v2.5라는 이름을 사용할 수 있게 된다. 이 이름을 타인과도 공유 하려면 (예를 들어, 릴리즈 번호라던지) tag 객체를 생성해야 하고, sign할 필요가 있을 수도 있다. 자세한 내용은 git-tag를 참고한다.

commit명을 인수로 받는 모든 git 명령에 이 이름을 사용할 수 있다.

$git diff v2.5 HEAD            #현재 HEAD와 v2.5와 비교
$git branch stable v2.5     #v2.5로부터 새로운 브랜치 "stable"을 브랜치한다.
$git reset --hard HEAD^   #현재 브랜치와 작업 디렉토리를 HEAD^상태로 되돌린다.


마지막 명령문은 주의해야 한다. 작업디렉토리의 변경내용뿐만 아니라 HEAD^이후의 모든 commit들까지 리셋된다. 리셋된 commit들이 다른 브랜치에도 존재하지 않다면 완전히 소실되는 것이 된다. 또한 다른 개발자들이 사용하고 있는 공개 브랜치를 reset하지 않도록 한다. reset할 경우 merge한 내용들까지 history에서 사라지게 되는 결과를 낳게 된다. 만약 push했던 내용을 되돌리고 싶다면 git-reset 대신에 git-revert를 사용하도록 한다.

git-grep 명령은 프로젝트의 모든 버젼으로부터 문자열 검색을 할 때 사용한다.

$git grep "hello" v2.5


는 v2.5에서 hello라는 문자열을 검색한다.
commit명을 생략하면 현재 디렉토리의 모든 파일에서 검색을 실행할 것이다.

git grep "hello"


는 git이 현재 tracking하고 있는 파일들에서 검색을 실행한다.
git의 많은 명령들을 commit들을 인수로 받는데 commit들을 지정할 수 있는 여러 방법이 있다. 여기 git-log의 예제가 있다.

$git log v2.5..v2.6      #v2.5와 v2.6사이의 commit들
$git log v2.5..              #v2.5이래의 모든 commit들
$git log --since="2 weeks ago"    #2주 전 이래의 모든 commit들
$git log v2.5.. Makefile       #v2.5이래에 Makefile에 변경을 가했던 모든 commit들


git-log에 지정할 수 있는 commit의 범위의 시작과 끝은 상속관계에 관계없이 지정할 수도 있다. 예를 들면, "stable-release"와 "master"라는 브랜치가 예전에 분기되었다고 하면

$git log stable..experimental


는 experimental 브랜치에는 존재하고 stable 브랜치에는 존재하지 않는 commit들을 출력한다. 반면에

$git log experimental..stable


는 stable 브랜치에는 존재하고 experimental에는 존재하지 않는 commit들을 출력한다.

git-log 명령에는 약점도 존재한다. commit의 이력을 일람으로만 출력한다는 점이다. 만약 이력에 분기되었다가 다시 merge되었다면 git-log가 출력하는 commit들의 순서는 의미가 없어지게 된다.

복수의 공헌자들이 작업하는 대부분의 프로젝트들(리눅스 커널등)은 많은 merge를 가지고 있다. 이럴 경우 gitk는 이력을 시각화하는데 더 유용할 것이다. 예를 들면

$gitk --since="2 weeks age" drivers/


는 최근 2주간의 drivers 디렉토리에 가한 commit들을 열람할 수 있게 한다. (주: gitk의 폰트 크기를 조절하려면 컨트롤키를 누를 상태에서 +나 -키를 누른다)

마지막으로, 파일명을 인수로 받는 git 명령의 경우, 파일명 앞에 commit명을 지정할 수 있다.

$git diff v2.5:Makefile HEAD:Makefile.in


는 v2.5의 Makefile과 HEAD의 Makefile.in의 차이를 출력한다.
마찬가지로 git-show라면

$git show v2.5:Makefile


라는 식으로 사용가능하다.

다음 단계


이 튜토리얼만으로도 프로젝트의 분산 리비젼관리를 하는 것에는 충분할 것이다. git의 깊이와 힘을 완전히 이해하기 위해서는 2개의 간단한 기본적인 원리에 대해서 이해해야 한다.

- 객체 데이터베이스  : 프로젝트( 파일, 디렉토리, commit들)의 이력을 저장하는 시스템
- index 파일 : 디렉토리의 상태를 캐싱하는 파일. commit을 생성하거나 작업 디렉토리를 check out하거나 merge에 관련된 여러 트리를 저장하는 곳

이 튜토리얼의 Part 2에서는 객체 데이터베이스와 index 파일과 그외 여러가지들에 대해서 설명한다.
Part 2로 바로 진행하고 싶지 않다면 여기 흥미로운 읽을만한 것들이 있다.

git-format-patch(1), git-am(1): These convert series of git commits into emailed patches, and vice versa, useful for projects such as the Linux kernel which rely heavily on emailed patches.

git-bisect(1): When there is a regression in your project, one way to track down the bug is by searching through the history to find the exact commit that's to blame. Git bisect can help you perform a binary search for that commit. It is smart enough to perform a close-to-optimal search even in the case of complex non-linear history with lots of merged branches.

gitworkflows(7): Gives an overview of recommended workflows.

Everyday GIT with 20 Commands Or So

gitcvs-migration(7): Git for CVS users.


SEE ALSO


원문: http://www.kernel.org/pub/software/scm/git/docs/gittutorial.html
번역: 허 련호(airless@funit.net)
2009/04 생성, 2011/08 수정
크리에이티브 커먼즈 라이센스
Creative Commons License
2011/12/26 10:58 2011/12/26 10:58
TAG
받은 트랙백이 없고, 댓글이 없습니다.

댓글+트랙백 RSS :: http://blog.hwport.com/rss/response/639

댓글+트랙백 ATOM :: http://blog.hwport.com/atom/response/639


본 내용은 "http://www.pptp.kr/docs/linux-client.htm" 에서 발췌한 것임을 밝힙니다.




ppTp VPN Linux client  설정 방법
    version 2.20
    글쓴이 : (주)리눅스랩 대표이사 배철수 <churl_nospam@linuxlab.kr> - 메일 보낼때는 _nospam 을 빼야 한다.

    사용자는 자신의 판단으로 아래 프로그램 설치 및 사용 여부를 결정해야 한다. 아래 설정으로 인해 리눅스 호스트의 오작동, 데이타 손실 등 어떠한 물적, 정신적 손해가 발생되도 이는 이 프로그램을 설치한 사람 자신의 책임이다.

    설치가 제대로 안된다고 PC를 발로 걷어차서 PC가 부서지거나 발가락이 삐는것, 머리카락을 쥐어 뜯어 대머리가 되는것, 또 밤새 설치하다가 늦잠자서 회사에 지각해 상사에게 꾸지람 듣고 홧김에 술마시고 음주운전하다 벌금을 무는 등의 일체의 간접적인 손해도 역시 본인의 책임이 아니다.

    . 이 글에서 리눅스 client 란 리눅스랩의 pptp VPN 서버를 연결하는 사용자 측 리눅스 호스트를 말한다.
    . 이텔릭체로 표시한 부분은 사용자가 입력하는 명령이다. (뒤에 <Enter> 키를 눌러야 한다.)
    . 회색 바탕은 리눅스 client 의 화면 출력이다.

    [사족]

    1. 리눅스랩의 라이브라 장비를 사용해서 리눅스랩과 동일한 VPN 고정 IP 서비스를 하는 다른 업체의 VPN 연결에는 설정을 사용할 수 없다. VPN 연결은 되지만 인터넷에서 리눅스 호스트를 접속할 수 없다.
    2. 아래 설명은 데비안 배포본을 기준으로 한다.
    3. 리눅스 전문가라면 아래 설명을 보지 않고도 pptp 를 연결할수 있다.
      아래 설명을 읽고도 pptp 를 연결하지 못한다면 리눅스를 사용하지 말고 윈도우를 사용하기를 권한다.
    4. 질문이 있으면 게시판에 글 올리기 바란다.
  1. 사전 준비 사항
    • 리눅스랩에서 " 개인 IP(공인 IP)" 테스트 계정과 패스워드를 받는다. (http://pptp.co.kr)

      *. 해외로밍 테스트 계정으로는 pptp 연결 까지만 테스트 가능하다. 그 이상(외부에서 할당 받은 IP로 연결 )을 테스트하려면 반드시 공인 IP를 받아야 한다.

    • 리눅스 client 에 아래 세 프로그램이 설치되어 있는가 확인한다. 디렉터리 위치도 아래와 같아야 한다.

      1. which pppd 
      /usr/sbin/pppd

      2. which pptp 
      /usr/sbin/pptp

      3. which ip 
      /sbin/ip

    • ls -l /etc 명령을 줄 때 아래와 같은 라인이 나와야 한다.

      drwxr-xr-x 3 root root 4096 Aug 18 18:17 ppp

    • ls -l /usr/sbin/pppd 명령을 주면 아래처럼 나와야 한다.

      -rwsr-xr-- 1 root dip 260920 2007-03-17 15:52 /usr/sbin/pppd

      맨 앞의 rws 가 중요하다. 만약 이 부분이 rwx 로나오면 아래 명령을 준다.

      chmod 4754 /usr/sbin/pppd

    • 공유기 사용시 주의 사항.

      아래의 모든 설정은 공유기를 사용하는 경우도 작동한다. 단 다음 두 가지를 확인해야 한다.

      1. 공유기에 pptp 통과(passthrough) 기능이 있는지 확인해야 한다. 매뉴얼에 pptp 통과 기능이 있다고 해서 그대로 믿어서는 안된다. 윈도우 PC 를 공유기 아래에 연결해서 VPN 연결이 돼는지 확인해 본다.

        본인이 아는바로는 국내에서 많이 사용하는 국산 공유기 대부분이 pptp 통과 기능이 없다. 자세한 사항은 자주받는 질문 3 번을 참고하기 바란다.

      2. 리눅스 client 에서 공유기의 로칼 포트로 ping 이 돼는지 그리고 인터넷 사이트(리눅스랩 VPN 서버의 IP)로 ping 이 돼는지를 확인해야한다.
    • 방화벽 밑에서 연결

      방화벽 사용시는 방화벽 업체에 pptp 통과 기능을 넣어 달라고 애기해야 한다.
      방화벽이 리눅스라면 커널을 컴파일해야 한다.

        [ /usr/sbin/pptp 컴파일 ]

    /usr/sbin/pptp (pptp client) 파일이 없으면 소스 파일 ( pptp-linux-1.4.0.tar.gz )을 다운받아 컴파일 하면 된다.

    1. 다운 받은 pptp-linux-1.4.0.tar.gz 파일을 /tmp 디렉터리에 넣는다.
    2. cd /tmp
    3. tar zxvf pptp-linux-1.4.0.tar.gz
    4. cd pptp-linux-1.4.0
    5. make
    6. make install
    위의 과정을 마치면 /usr/sbin 디렉터리에 pptp 란 파일이 만들어 진다.
    컴파일이 안된다면 RedHat Fedora Core release 3 (Heidelberg) 용 pptp binary file 을 다운받아 설치해 본다. 단 관련 라이브러리가 설치되어 있어야 한다.

    *. 마우스 우측 버튼으로 위의 링크를 클릭해 다운 받은 후 /usr/sbin 에 넣고
    chmod 755 /usr/sbin/pptp 명령을 준다.

  2. 관련 사이트/글
    1. pptp client : http://pptpclient.sourceforge.net
    2. pppd : pppd 설치
    3. pppd : ppp 사용
  3. [주 의 ]

    리눅스 client 에 pppoe 방식의 ADSL 모뎀을 직접 연결한 경우에는 특별한 주의가 필요하다.(ADSL 모뎀을 공유기에 연결한 경우는 상관없다.) pppoe 와 pptp 모두 ppp 프로토콜을 사용하므로 일부 설정 파일이 충돌할 수 있다.

    *. 최근에 나오는 일부 ADSL 과 VDSL 은 pppoe 프로토콜을 사용하지 않는다. DHCP(IP 자동 부여)를 사용한다.

    리눅스에서 pppoe 프로그램을 사용해서 ADSL 라인을 연결했다면 아래 "5." 번 명령 실행시 pppoe 프로그램이 정상적으로 작동하지 않을 수도 있다. 따라서 아래 명령 실행 전에 필히 /etc/ppp 디렉터리를 백업 받아야 한다.
    • cd /etc
    • cp -a /etc/ppp /etc/ppp.bak

    문제 발생시 원상태로 복원 하려면

    • cd /etc
    • rm -rf ppp
    • cp -a ppp.bak ppp

    linux shell script 를 잘 안다면 아래 3 번의 스크립트를 작성해서 부팅 때 자동 실행되는 파일에 넣는다.

    아래는 리눅스가 공유기 아래에 있고 IP 가 192.168.1.x 인 경우이다.

    3 번 스크립트가 실행 되려면 아래 두 파일이 필요하다.

    1. /etc/ppp/chap-secrets

      이 파일에 들어갈 내용이다.

      lab * pass *

      - lab 은 리눅스랩 VPN account, 3 번 스크립트의 LOG_NAME 뒤와 같다.
      - pass 은 리눅스랩 VPN 암호

    2. /etc/ppp/peers/linuxlab

      파일 내용

      pty "/usr/sbin/pptp w.x.y.z --nolaunchpppd"

      - w.x.y.z 는 리눅스랩 VPN 서버 주소

    3. 부팅 때 실행되는 스크립트
      #!/bin/bash
      
      LOG_NAME="lab"
      /usr/sbin/pppd ipparam linuxlab name $LOG_NAME call linuxlab
      
      sleep 5
      
      netstat -nr | grep ppp >/dev/null 2>&1
      
      if [ $? == 0 ]; then
        ADDR=`ip addr sh eth0 | grep -w inet | awk -F' +|/' '{print $3}'`
        DGW=`netstat -nr | grep -w '^0.0.0.0' | grep eth0 | awk '{print $2}'`
      
        if [ -n "$ADDR" -a -n "DGW" ]; then
           ip rule add from $ADDR table 1 prio 10
           ip ro repl table 1 default via $DGW dev eth0
        fi
      else
        exit
      fi
      
      ip ru sh | grep -w $ADDR
      
      if [ $? == 0 ]; then
         tem=`netstat -nr | grep ppp | awk '{print $1","$8}'`
         gate=`echo $tem | cut -d',' -f1`
         dev=`echo $tem | cut -d',' -f2`
      
          [ -n "$gate" -a "$dev" ] && \
          ip ro repl default via "$gate" dev "$dev"
      fi
      
      
  4. 아래 설정은 root 권한이 필요하다.
  5. 리눅스 client 설정 프로그램 (linuxlab-pptpc.tar)을 /tmp 디렉터리에 다운 받는다.
  6. tar  xvf  /tmp/linuxlab-pptpc.tar  -C/etc/ppp 명령을 실행

    ( -C/etc/ppp 에서 C 는 대문자이다. 중간에 공백이 없다.)

    • /etc/ppp 디렉터리에 아래 다섯 파일이 생성된다.(아래 파일이 안 나타나면 tar 명령이 틀렸다.)

      chap-secrets  chk-vpn  cron-pptpc  ip-up  pptpc

    • /etc/ppp/peers 에 아래 파일이 생성된다.

      linuxlab

    [참 고] : 각 파일의 설명

    파일명mode설명
    chap-secrets-rwx------인증 파일. 계정과 패스워드가 들어간다.
    chk-vpn-rwxr-xr-xVPN 연결이 끊겼는 지를 체크해서 재연결해주는 쉘 스크립트.
    cron-pptpc-rwxr-xr-x1 분에 한번 chk-vpn 스크립트를 실행하는 crontab 설정파일
    pptpc-rwxr-xr-xVPN 을 연결하는 스크립트
    linuxlab-rwxr-xr-xpptp 옵션 및 서버 주소 지정
    ip-up-rwxr-xr-xVPN 연결시 실행되는 명령 지정
  7. /etc/ppp 디렉토리 내의 다음 파일을 수정한다.

      리눅스랩에서 받은 VPN 서버의 계정이 test999 패스워드가 1234 라면

    1. chap-secrets 파일에 아래 라인을 추가한다.

        test999    *   1234   *

         ("account   *   pass   *" 란 줄은 예제이다. 즉 이런 식으로 입력한다는 뜻임.)

    2. pptpc 파일 중 LOG_NAME="account" 에서 account 대신 리눅스랩에서 받은 ppTp VPN 계정을 넣는다.

        LOG_NAME="test999"

    3. /etc/ppp/peers/linuxlab 파일의 아래 라인에 리눅스랩 VPN 서버의 주소가 들어가야 한다.

      pty "/usr/sbin/pptp w.x.y.z --nolaunchpppd"

      ex) pty "/usr/sbin/pptp 118.128.29.8 --nolaunchpppd"

      위의 w.x.y.z 에는 리눅스랩에서 할당하는 공인 IP를 입력하는게 아니다. 할당한 IP 는 pptp 가 연결이 될 경우에 client 에서 받는 IP 주소이다. 서버 주소는 리눅스랩 홈페이지(pptp.kr)에 나오는 VPN 서버의 IP 주소를 입력해야 한다. (도메인 네임은 사용할 수 없다.)

      공인 IP 는 반드시 지정된 서버만 접속해야 한다.

    *. 만약 ADSL 라인을 pppoe 방식으로 리눅스 client 에 연결했다면 /etc/ppp/peers/linuxlab 파일에 아래 라인을 추가한다.

    mtu 1452

    *. vi 편집기를 사용할 줄 모르면 pico 나 nano 를 사용하면 된다. 리눅스에서 편집이 어려우면 ftp 로 다운 받은 후 윈도우에서 수정한다. 단 윈도우로 다운 받거나 윈도우에서 리눅스로 업로드할 때 ftp 에서 꼭 ascii 옵션을 사용해야 한다. 아래와 같다. (윈도우에서 pptpc 파일을 업로드 했으면 필히 "chmod 755 pptpc" 명령을 줄것.)

      230 User admin logged in.
      Remote system type is UNIX.
      Using binary mode to transfer files.
      ftp> ascii
      200 Type set to A
    
  8. 연결

    /etc/ppp/pptpc  명령을 실행한다.

  9. 연결 확인
    1. ifconfig  명령을 주었을 때 아래처럼 나와야 한다.(일부 생략)
       ppp?     Link encap:Point-to-Point Protocol
                inet addr:999.999.999.999  P-t-P:w.x.y.z  Mask:255.255.255.255

      *. ip 명령을 사용한다면 ip addr sh  명령을 주면 아래처럼 나온다.

          14: ppp0:  mtu 1500 qdisc pfifo_fast qlen 3
                 link/ppp
                 inet 999.999.999.999 peer w.x.y.z scope global ppp0

      *. 999.999.999.999 대신 자신에게 할당된 IP 주소가 나와야 한다.

    2. ps ax  명령을 줄 때 아래 3 줄이 나와야 한다.
        16254   ?   Ss   0:00 /usr/sbin/pppd ipparam linuxlab  name ????? call linuxlab
       16255   ?   S    0:00 pptp: GRE-to-PPP gateway on /dev/ptmx
       16289   ?   S    0:00 pptp: call manager for w.x.y.z
        (or 629 ?   S    0:00 pptp: call manager for 220.--nolaunchpppd)
      *. ????? 대신 자신의 VPN 계정이 나와야 한다.
      *. "16254" 는 Process ID 로서 VPN 연결을 끊을 때 필요하다.
    3. netstat -nr 명령으로도 확인이 가능하다.
        linuxlab:/etc/ppp# netstat -nr
        Kernel IP routing table
        Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
        172.29.0.1      0.0.0.0         255.255.255.255 UH        0 0          0 ppp0
        192.168.0.0     0.0.0.0         255.255.255.0   U         0 0          0 eth1
        0.0.0.0         192.168.0.1     0.0.0.0         UG        0 0          0 eth1
      위 라우팅 테이블은 리눅스랩 방화벽 밑에서 VPN 을 연결한 경우이다. 인터넷 라인(방화벽)은 eth1 에 연결돼 있다.
      1. 4 번째 줄의 "172.29.0.1" 은 VPN 서버의 IP 이다. 어느 서버를 연결하느냐에 따라 다르다.
      2. 5 번째 줄의 192.168.0.0 은 방화벽 과 리눅스 client 간의 내부 네트워크 주소이다.

      리눅스 네트워킹에 대해 조금 알고 있는 리눅서라면 위의 라우팅 테이블이 이상하다고 느낄 것이다.

      *. 이상하지 않다고 생각했다면 리눅스 네트워킹에 대한 전문가이거나 문외한 중 하나이다.

      왜 디폴트게이트웨이 인터페이스(마지막 줄)가 eth1 인지 의아해 할것이다.
      default gateway 가 VPN 인터페이스(ppp0) 여야 하지 않는가?
      라우팅 테이블이 틀리지 않은가?

      천만에요. 이걸 비대칭 라우팅이라고 부르는 겁니다. 일반적으로 거의 사용하지 않는 어려운 기법입니다.
      (비대칭 라우팅은 개념이 어려워 여기서 설명하지 않습니다.)

      외부에서 리눅스랩에서 받은 IP 주소로 telnet 이나 ssh 로 연결해 보세요.

  10. 부팅시 vpn 자동 연결

    위의 연결 명령을 리눅스 boot 스크립트에 넣어야 리부팅시 자동으로 VPN 이 연결된다.

    1. Redhat

      /etc/rc.d/rc.local 파일의 맨 뒤에 아래 라인을 넣는다.

      /etc/ppp/pptpc

    2. Debian

      ln -s /etc/ppp/pptpc /etc/rc.boot/pptpc 명령 실행

      또는 /etc/ppp/pptpc 을 /etc/rc.boot 에 복사한다.

    3. SuSE

      /etc/init.d/boot.local 파일의 맨 뒤에 /etc/ppp/pptpc 라인을 추가한다.

      

    주의 : 다른 배포본 또는 customized 된 시스템에서 연결시 VPN 연결 명령(pptpc)이 인터넷 연결 프로그램 보다 나중에 실행되야 한다.

  11. 자동 재연결

    인터넷 라인이 끊기면 VPN 연결도 같이 끊긴다. 또는 공유기를 사용시 공유기를 껏다 켜도 VPN 연결은 끊긴다. VPN 연결이 끊겨도 자동으로 재 접속되게 하려면 crontab 에서 /etc/ppp/chk-vpn 파일을 1분에 한번씩 자동으로 실행되게 해야 한다.

    crontab -l 명령을 주면 현재 crontab 에서 실행 되는 명령이 나온다. 아무런 설정이 없다면 아래 명령을 준다.

    crontab /etc/ppp/cron-pptpc <enter>

    crontab 에 이미 다른 설정이 있다면 crontab -e 명령으로 crontab 에 아래 라인을 추가한다.

    */1  *  *  *  *  /etc/ppp/chk-vpn

    각 항목 다음에 반드시 공백이 한 개 이상 필요하다.

    확인하려면 crontab -l  명령을 준다. 아래처럼 나와야 한다.

    */1  *  *  *  *  /etc/ppp/chk-vpn

    *. crontab 설정 확인 : crobtab -l
    *. crontab 설정 제거 : crontab -r

  12. 장애 처리
    1. VPN 연결이 안된다.
      1. 공유기를 사용한다면 공유기가 "VPN (ppTp) pass through" 기능을 지원하는지 확인할 것.(공유기/방화벽 사용에 대해서는 자주 받는 질문 "3" 번을 참고하시오.) : 공유기 문제인지 확인하려면 공유기를 떼고 연결을 시도하던지 또는 윈도우에서 VPN 연결을 시도해 본다.
      2. /etc/ppp/options 파일을 지우고 연결을 시도한다.
      3. man pppd 명령을 주어서 옵션에 -defaultroute 가 있는지 확인해 본다. 오래된 배포본의 경우 -defaultroute 대신 nodefaultroute 를 사용하는 경우도 있다. 이 경우는 /etc/ppp/peers/linuxlab 파일의 -defaultroute 를 nodefaultroute 로 변경
      4. 그래도 안되면 /etc/ppp/options 파일에 debug 을 넣고 연결을 시도한다. 그러면 /var/log/debug(or syslog : 배포본 마다 다르다) 파일에 ppp 와 관련된 로그가 저장된다. 그 로그를 자세히 보면 어디엔가 에러 원인이 나타난다

        이 글 맨 뒤에 정상적으로 연결되었을 때의 /var/log/debug 파일 내용을 첨부했다. 이 로그와 비교해서 차이가 있는지 본다. 로그에 "Welcome to libra." 가 안나오면 계정 또는 패스워드가 틀렸다.

    2. VPN 연결은 되는데 자주 끊긴다.

      - 공유기가 "VPN (ppTp) pass through" 기능을 제대로 지원하는지를 확인할 것.

      - 윈도우 PC에서 VPN 을 연결해도 마찬가지 인지를 비교해 볼것.

      - 라인 상태를 점검해 본다. 리눅스랩 VPN 서버 IP 주소 또는 사용하는 인터넷 라인의 ISP 의 DNS 서버 주소로 ping 테스트를 해 본다.

    3. 일부 사이트가 열리지 않거나 큰 파일 다운로드시 속도가 느리다.

      - /etc/ppp/peers/linuxlab 파일에 아래 라인을 추가해 본다.

           mtu 1432 (또는 mtu 1400)

    4. VPN 연결은 되는데 외부에서 리눅스 client IP 주소로 접속(ping)이 안된다.
      1. /sbin/ip route show  명령을 줄 때 에러가 뜨는지를 볼것. : 에러가 뜨면 iproute 프로그램을 재설치한다.
      2. 방화벽 룰이 설정되었는지를 확인할것.(iptables -vnL) 방화벽 룰을 제거하고(아래 명령) 연결을 시도해 본다.

        iptables -F

      3. dmesg  명령을 실행해서 에러가 나오는지를 볼것

        혹시 아래와 비슷한 에러가 나오면 이는 배포본, 커널, 모듈 문제이므로 해결이 쉽지 않다.

        divert: no divert_blk to free, ppp0 not ethernet
        divert: not allocating divert_blk for non-ethernet device ppp0

        <해결 방법>

        - 아래 두 명령을 실행해 본다.(아래 명령은 한 줄이다. \는 명령을 두 줄로 계속할 때 사용한다.)

        (1) /sbin/iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN \
            -j TCPMSS --clamp-mss-to-pmtu
         
        (2) /sbin/iptables -A OUTPUT -p tcp --tcp-flags SYN,RST SYN \
            -j TCPMSS --clamp-mss-to-pmtu

        - 랜카드 드라이버를 모듈로 올리지 말고 커널을 컴파일해 넣는다.

    5. 인터넷 라인이 끊겼다 다시 연결됐는데 pppd 데몬이 종료하지 않아서 VPN 자동 재연결이 안된다.

      linuxlab 파일에는 pppd 옵션을 지정하는 부분이 들어 있다. 이중 lcp-echo-interval 과 lcp-echo-failure 는 VPN 연결이 비정상 종료(라인 절단)인 경우에 pppd 데몬을 종료케하는 부분이다.

      lcp-echo-interval 은 몇 초에 한 번 VPN 서버로 연결 확인(LCP Echo) 을 할것인지를 지정한다. lcp-echo-failure 는 LCP Echo 가 몇 번 실패할 경우에 pppd 데몬을 종료할 것인지를 지정한다. lcp-echo-interval 30 이고 lcp-echo-failure 가 4 라면 라인 연결이 끊기면 약 120 초 이내에 pppd 데몬이 종료한다. 그러면 crontab 에서 chk-vpn 스크립트가 pppd 데몬이 종료했으므로 pptpc 스크립트를 실행해서 VPN 을 다시 연결해 준다.

      그러나 극히 드믈지만 위의 두 옵션에서 pppd 데몬이 죽지 않는 경우도 있다. 이럴 때를 대비해 만든 옵션이 있는데 바로 idle 이다. "idle 240" 을 linuxlab 에 추가하면 이 문제를 해결할 수 있다. 이 옵션은 240초 동안 전혀 데이타 이동이 없으면(LCP Echo 포함) pppd 를 종료하라는 뜻이다.

    6. 모든 방법을 동원해도 연결이 안되면 /etc/ppp/options 파일에 debug 를 넣고 연결을 시도 후 /var/log/debug 파일 내용[참고 4]을 vpn@linuxlab.co.kr 로 보내기 바란다.

      그리고 공유기에서 23/tcp(또는 22/tcp) 를 리눅스 client 로 포트포워딩을 설정하고 리눅스에서 telnet(ssh) 데몬을 가동하면 리눅스랩에서 원격으로 접속하여 모든 설정을 해 줌.(비용 없슴.)

      [참 고]

      리눅스랩 VPN 을 연결해서 메일 서버를 운영하는데 한메일 같은 일부 포탈 사이트로 보낸 메일이 스팸으로 처리된다는 불평을 하는 사람이 있다. 이는 비대칭 라우팅을 이해하지 못하기 때문이다.

      한메일에서 리눅스 메일 서버로 보내는 메일은 VPN 으로 받은 IP 주소로 들어온다. 하지만 윈도우에서 Outlook 로 리눅스 메일 서버를 지정해 메일을 보내면 리눅스 메일 서버가 일단 저장 한 후 이를 다른 메일 서버(한메일 메일서버)로 보낸다.

      리눅스 메일서버가 비대칭 라우팅을 사용한다면 메일이 나갈 때는 리눅스랩에서 VPN 으로 받은 IP 주소가 사용되지 않고 공유기 또는 리눅스에 연결된 ADSL/케이블 라인의 IP 주소로 나간다.

      만약 메일 수신자측 메일 서버가 메일 헤더에 있는 도메인 네임을 nslookup 하면 리눅스랩의 VPN IP 주소가 나올것이다. 그런데 메일헤더에 있는 IP 주소는 VPN IP가 아닌 ADSL 라인의 IP 이다. 일부 포탈사이트는 이 둘이 같지 않으면 스팸으로 처리한다.

      불특정 다수에게 메일을 발송하는 경우라면 [참고 1] 에 나온 것처럼 비대칭 라우팅이 아닌 방식을 사용하기 바란다. 단 이 경우는 속도에서 손해를 본다. 리눅스 메일 서버에서 메일이 나갈 때 ADSL/케이블 라인의 속도가 아닌 리눅스랩 VPN 서버에서 지정한 속도가 적용되기 때문이다. (리눅스랩 VPN 서버는 대역폭 제어를 한다.)

  13. 연결 끊기

    위의 6. 2) 첫번째 줄 맨 앞에 나오는 프로세스 ID를 kill 한다.

    kill -9 16254

  14. 공유기(NAT gateway) 사용

    공유기 사용자는 필히 자주 받는 질문을 참고하기 바란다. 리눅스를 NAT gateway 로 사용하는 경우도 마찬가지다.

  15. 기 타

    위의 설정은 리눅스 client 에서 웹/메일/DB 등의 서버를 운영할 경우에 적합하다. 즉 위의 설정으로 연결하면 외부에서 리눅스 client 의 VPN 으로 받은 고정 IP 주소로 연결시만 VPN 링크가 사용된다.

    리눅스 client 에서 인터넷을 사용할 경우 또는 리눅스 client 를 게이트웨이로 지정해서 인터넷을 사용하는 피시에서 인터넷을 사용시는 VPN 으로 부여 받은 고정 IP가 사용되지 않고 리눅스 client 에 연결된 인터넷 라인의 IP(공유기 사용시는 공유기에 부여된 IP 주소) 가 사용된다.

    (비대칭 라우팅으로 불린다. 왜 그렇게 되는지 설명하려면 너무 길다.). [참고 3]을 보면 default gateway 가 공유기의 주소 즉 192.168.0.1 이다.

    사용자의 입장에서는 리눅스 client 에서 데이타가 나가는 경우에는 VPN 서버를 거치지 않는게 유리하다. 왜냐하면 VPN 서버를 거치면 서버에서 대역폭 제어를 하므로 해당 라인의 최고 속도를 사용할 수 없다.

    따라서 이 설정을 효과적으로 사용하려면 VPN Pass Through 기능이 있고 Multi-Session 을 지원하는 공유기(Dlink 사의 DI-604 등)에 인터넷 라인을 연결하고, 리눅스 client 에는 사설 IP(192.168.x.x)를 부여해서 리눅스 client 의 디폴트 게이트웨이를 공유기의 랜 포트의 주소를 주면 된다.

    공유기 설정에서 공유기에 연결된 피시의 IP 를 자동으로 부여 하지 말고 수동으로 부여해야 한다. 아니면 공유기에서 IP 자동 부여를 하되 VPN 을 연결하는 리눅스 client IP 를 자동 부여 IP 목록에서 제외한다.

    물론 위의 설정은 리눅스 client 에 직접 ADSL/케이블/전용선을 연결할 경우에도 작동한다.

    질문이 있으면 vpn@linuxlab.co.kr 로 문의하기 바란다.

    ppTp 자주 받는 질문 에도 관련 내용이 있다.

[참고 1] 아래 부분 매뉴얼은 아직 수정이 진행중입니다.
*. 리눅스 client에서 데이타를 내보낼때도 VPN IP로 나가게 하려면

위의 설정은 VPN을 연결한 리눅스 client 에서 인터넷을 연결하는 경우는 VPN IP를 사용하지 않는다.(비대칭 라우팅이라 불린다.) 리눅스랩의 VPN 서버는 대역폭 제어(QOS)를 하는데, 리눅스 client 에서 인터넷을 연결할 때도 VPN 으로 받은 IP 주소를 사용하려면 데이타가 VPN 서버를 거쳐야 하고, 그 경우 업로드/다운로드 속도가 VPN 서버에서 지정한 속도로 제한을 받기 때문이다.

즉 "개인 IP" 512k 서비스를 신청했다면 VDSL 10 메가 라인을 사용해도 512k 이상을 낼 수 없다. 그래서 리눅스 client 에서 인터넷을 연결시는 VPN 서버를 거치지 않게한 것이다. (외부에서 VPN 으로 받은 IP 주소로 리눅스 client 를 연결할 때는 당연히 VPN 서버를 거친다.) VPN 서버를 거치지 않으면 그 라인의 최고 속도를 다 이용할 수 있기 때문이다.

즉 리눅스 client 가 메일 서버이고 호스트 네임이 mail.pptp.co.kr 이라면 외부에서 mail.pptp.co.kr 로 메일을 보낼 때는 VPN 주소가 사용되지만 mail.pptp.co.kr 에서 다른 메일 서버로 메일을 보낼 때는 VPN 으로 받은 IP 주소가 아닌 리눅스 client나 공유기의 ADSL/케이블 라인의 IP 주소가 사용된다.

그러나 리눅스 client 에서 인터넷의 호스트를 연결하는데 그 호스트에서 상대방의 IP를 체크해서 특정 IP 만 연결을 허용한다면 리눅스 client 에서 데이타를 내 보낼때도 VPN IP로 나가야한다. 즉 VPN 서버를 거쳐야 한다.(이런 경우는 많지 않을 것이다.)

이 설정은 상당히 까다롭다. 왜냐하면 이렇게 하려면 리눅스 client의 default gateway 를 VPN Interface(ppp?) 로 변경해야하는데 이 때 단순히 게이트웨이를 변경하면 looping 이 발생한다.

물론 리눅스는 이 경우에도 당연히 설정이 가능하다. 여기서 방법을 설명하려면 복잡하므로 아래의 설정 프로그램을 다운받아 설치하면 자동으로 해결된다.

<순서>

  1. awk 프로그램이 설치되어 있어야 한다.
  2. 위의 4번 까지 진행한다.
  3. ip-dgw.tar 파일을 다운받는다.
  4. 이 파일을 /etc/ppp 디렉터리에 넣고 tar xvf ip-dgw.tar 명령을 준다.
    ip-up 과 ip-down 두 파일이 나타난다.
즉 본문 "3" 번에서 설치된 /etc/ppp 디렉터리의 ip-up 파일을 새 파일로 대치하고 ip-down 파일을 하나 더 추가한 것이다. 다른 부분은 같다.

이젠 VPN을 연결하면 디폴트 게이트웨이가 VPN interface 로 변경될 것이다. 그리고 VPN 연결을 끊으면 연결 전의 라우팅 테이블로 복귀한다.

♣. VPN 연결 후의 라우팅 테이블은 아래와 같을 것이다.
    (리눅스 client 가 공유기 하단에 위치하고 192.168.1.x IP 주소를 사용하는 경우.)

bae@samba:~$ netstat -nr
Kernel IP routing table
Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
220.230.5.3     0.0.0.0         255.255.255.255 UH       40 0          0 ppp0
192.168.1.0     0.0.0.0         255.255.255.0   U        40 0          0 eth0
127.0.0.0       0.0.0.0         255.0.0.0       U        40 0          0 lo
0.0.0.0         220.230.5.3     0.0.0.0         UG       40 0          0 ppp0

♣. 비대칭 라우팅을 사용하는 경우의 라우팅 테이블은 "[참고 3]" "1." 에 나와 있다.

[주의]
ip-dgw.tar 를 설치하지 않고 디폴트 게이트웨이를 VPN 으로 변경하면 어떤 결과가 발생하는지 알고 싶으면 시험해 보십시오. 아마도 피시를 리부팅해야 할겁니다. VPN 연결만 끊는다고 해결되지 않습니다.

[버그] eth0 랜카드에 IP 주소가 두 개인 경우는 이 설정이 작동하지 않습니다. 리눅스랩으로 연락해서 수정 파일을 받아야 합니다.

[참고 2]

방화벽 내에서 ppTp VPN 연결

방화벽 내에서 사설 IP를 사용하는 경우에(NAT 방식 방화벽) 방화벽 내의 사설 IP를 사용하는 PC에서 ppTp VPN 을 연결하려면 방화벽에서 VPN 서버 주소(tiger 의 경우 220.230.5.5)로 부터의 ip protocol 47(GRE) 및 tcp source port 1723 번을 VPN을 연결하는 PC의 IP 주소로 port forwarding 해 주어야 한다. (방화벽의 DMZ 구간에 연결할 경우는 상관 없다.)

방화벽이 리눅스라면 아래 설정이 필요하다.
(리눅스를 공유기로 사용하는 경우도 같다.)

방화벽의 인터넷 쪽 인터페이스가 eth1, IP 주소는 222.x.129.9,
방화벽의 랜 쪽 인터페이스가 eth0, IP 주소는 192.168.1.1
VPN 을 연결하는 리눅스 서버의 IP 주소가 192.168.1.7 이라면

  1. iptables -t nat -I POSTROUTING -o eth1 -j SNAT --to 222.x.129.9
    (또는 iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -j MASQUERADE)
  2. iptables -t nat -I PREROUTING -i eth1 -p 47 -d 222.x.129.9 -j DNAT --to 192.168.1.7
  3. iptables -t nat -I PREROUTING -i eth1 -p tcp --sport 1723 -d 222.x.129.9 -j DNAT --to 192.168.1.7
 
[참고 3]
실제 적용 예
[리눅스 client 의 환경]

공유기에 ADSL/VDSL/케이블 모뎀이 연결되어 있고 공유기의 로칼 주소는 192.168.0.1, 리눅스 서버의 주소가 192.168.0.111, 리눅스 첫번째 랜카드(eth0)가 공유기에 연결돼어 있다.(아래 그림 참조)

  1. [Redhat]

    1. /etc/sysconfig/network-scripts/ifcfg-eth0

    DEVICE=eth0
    ONBOOT=yes
    BOOTPROTO=static
    IPADDR=192.168.0.111
    NETMASK=255.255.255.0
    GATEWAY=192.168.0.1
    

    2. /etc/sysctl.conf 파일

    net.ipv4.ip_forward = 1

    *. 리눅스 client 를 route 로 겸용할 경우에 필요하다.

    3. 위의 설명대로 리눅스랩 ppTp VPN client 프로그램 설치 후 /etc/ppp/pptpc 명령을 실행(또는 리부팅)해서 VPN 연결

    4. VPN 연결 후의 라우팅 테이블 및 인터페이스 주소는 아래와 같다.

    [root@linuxlab] netstat -nr
    Kernel IP routing table
    Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
    220.230.5.3     0.0.0.0         255.255.255.255 UH        0 0          0 ppp0
    192.168.0.0     0.0.0.0         255.255.255.0   U         0 0          0 eth0
    0.0.0.0         192.168.0.1     0.0.0.0         UG        0 0          0 eth0
    

    리눅스 네트워킹을 아는 사람들은 왜 마지막 줄의 디폴트 게이트웨이가 ppp0 가 아니고 eth0 인지 의아해 할것이다. 이는 외부에서 리눅스의 VPN IP 로 연결은 가능하게하고 리눅스에서 다른 인터넷 사이트를 연결할 때는 ADSL/케이블 모뎀 IP 주소로 나가도록 policy routing 기법을 사용하기 때문이다.

    [root@linuxlab] ifconfig
    eth0      Link encap:Ethernet  HWaddr 00:11:D8:52:0F:D4
              inet addr:192.168.0.111  Bcast:192.168.0.255  Mask:255.255.255.0
              UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
    
    lo        Link encap:Local Loopback
              inet addr:127.0.0.1  Mask:255.0.0.0
              inet6 addr: ::1/128 Scope:Host
              UP LOOPBACK RUNNING  MTU:16436  Metric:1
    
    ppp0      Link encap:Point-to-Point Protocol
              inet addr:59.150.255.xxx  P-t-P:220.230.5.3  Mask:255.255.255.255
              UP POINTOPOINT RUNNING NOARP MULTICAST  MTU:1500  Metric:1
    
    
              *. 화면 출력 중 불필요한 부분은 생략했다..
    
  2. [Debian]

    Debian 사용자라면 설명이 필요 없을 것 같다.

  3. 기타

    위와 같이 구성하려면 공유기에 VPN(ppTp) Pass Through 기능이 있어야 한다. 그리고 공유기가 Multi-Session(multi port) 을 지원한다면 공유기에 연결된 다른 PC(192.168.0.2~3) 에서도 동시에 VPN 연결이 가능하다. 위의 경우에는 공유기의 랜 포트에 연결된 PC 에 IP 자동 부여(DHCP)를 사용해도 된다.

    공유기에 VPN Pass Through 기능이 없으면 DMZ 를 사용해야 한다. 공유기에서 DMZ 를 설정하려면 공유기에 연결된 모든 PC(리눅스도 포함)의 IP를 수동으로 부여해야 한다. 그리고서 공유기에서 DMZ IP 주소를 리눅스 client 의 IP 주소로 지정한다.(DMZ를 사용한다면 한 대에서만 VPN 연결이 가능하다.)

  4. 참고자료

    리눅스랩 ppTp 사이트 
    ppTp 자주받는 질문 
    라이브라 L4 Switch

[참고 4]
정상적인 연결이 이루어 졌을 때의 client 쪽 /var/log/debug 파일 내용 
(배포본에 따라서는 syslog, daemon.log 에 나올 수도 있다.)

*. /etc/ppp/options 에 debug 를 넣을 것.

Oct 10 15:15:40 samba pppd[25626]: using channel 8
Oct 10 15:15:41 samba pppd[25626]: sent [LCP ConfReq id=0x1    ]
Oct 10 15:15:41 samba pppd[25626]: rcvd [LCP ConfReq id=0x1     ]
Oct 10 15:15:41 samba pppd[25626]: sent [LCP ConfAck id=0x1     ]
Oct 10 15:15:41 samba pppd[25626]: rcvd [LCP ConfAck id=0x1    ]
Oct 10 15:15:41 samba pppd[25626]: sent [LCP EchoReq id=0x0 magic=0x4adeccad]
Oct 10 15:15:41 samba pppd[25626]: rcvd [LCP EchoReq id=0x0 magic=0x2dcd9a7b]
Oct 10 15:15:41 samba pppd[25626]: sent [LCP EchoRep id=0x0 magic=0x4adeccad]
Oct 10 15:15:41 samba pppd[25626]: rcvd [CHAP Challenge id=0x1 \
<98672bc235a6dee925c9c372ef5d073ec561a384>, name = "libra"]
Oct 10 15:15:41 samba pppd[25626]: sent [CHAP Response id=0x1 \
<13b202bcab861afd11bd91702c18e701>, name = "admin"]
Oct 10 15:15:41 samba pppd[25626]: rcvd [LCP EchoRep id=0x0 magic=0x2dcd9a7b]
Oct 10 15:15:41 samba pppd[25626]: rcvd [CHAP Success id=0x1 "Welcome to libra."]
Oct 10 15:15:41 samba pppd[25626]: sent [IPCP ConfReq id=0x1  ]
Oct 10 15:15:41 samba pppd[25626]: rcvd [IPCP ConfReq id=0x1  ]
Oct 10 15:15:41 samba pppd[25626]: sent [IPCP ConfAck id=0x1  ]
Oct 10 15:15:42 samba pppd[25626]: rcvd [IPCP ConfNak id=0x1 ]
Oct 10 15:15:42 samba pppd[25626]: sent [IPCP ConfReq id=0x2  ]
Oct 10 15:15:42 samba pppd[25626]: rcvd [IPCP ConfAck id=0x2  ]
Oct 10 15:15:42 samba pppd[25626]: Script /etc/ppp/ip-up started (pid 25630)
Oct 10 15:15:42 samba pppd[25626]: Script /etc/ppp/ip-up finished (pid 25630), status = 0x0

*. 마지막 줄의 "status = 0x0" 에서 0x0 대신 다른 코드가 나오면 debug 파일 내용과 사용 배포본, 커널 버전(uname -a), pptp 버전(pptp 명령을 준다.), pppd 버전 등을 메일(위의 메일 주소에서 _nospam 을 제거)로 보내 주십시오.
이 에러가 뜨면 VPN 을 연결해도 외부에서 VPN 으로 받은 IP로 접속이 안된다.

L4, QOS, Firewall, VPN 기능을 모두 갖춘 국내 유일의 L4 Switch Libra

LinuxLab          http://www.linuxlab.co.kr

크리에이티브 커먼즈 라이센스
Creative Commons License
2011/09/15 09:33 2011/09/15 09:33
TAG , , , ,
받은 트랙백이 없고, 댓글이 없습니다.

댓글+트랙백 RSS :: http://blog.hwport.com/rss/response/636

댓글+트랙백 ATOM :: http://blog.hwport.com/atom/response/636


본 내용은 "http://www.funit.net/git" 에서 발췌한 것임을 밝힙니다.



git 튜토리얼 메뉴얼 페이지

개요

이 튜토리얼은 프로젝트를 임포트하고, 수정하고, 수정한 결과를 다른 개발자들과 공유하는 방법을 설명한다.

최신 버젼의 소스를 테스트하기 위해서 라든지 git으로 프로젝트를 패치해 오는 것에만 관심이 있는 경우, Git User's Manual의 첫 두 장을 읽어 보는 편이 좋을 것이다.

먼저, 명령어에 대한 문서를 보려면 man또는 git help를 사용한다. 예를 들어 git log --graph라는 명령의 도움말을 보려면:

  $man git-log

또는:

  $ git help log


라고 하면 된다.

git help를 사용할 경우에는 자신이 지정한 메뉴얼 뷰어로 보는 것이 가능하다. 자세한 내용은 get-help 메뉴얼을 참고한다.

git 으로 작업을 하기 전에 자신에 대한 정보(이름과 email)를 미리 설정에 두는 것이 좋다. 아래와 같이 하면 된다.

$git config --global user.name "여기에 이름을..."
$git config --global user.email you@yourdomain.example.com

새 프로젝트 임포트하기


처음으로 작성한 project.tar.gz라는 프로젝트 압축파일이 있다고 하자. 아래와 같이 git 버젼관리로 임포트해서 버젼 관리를 시작할 수 있다.

$tar xzf project.tar.gz
$cd project
$git init


압축을 풀고 그 디렉토로로 가서 git을 초기화하였다.
아래와 같이 결과가 출력될 것이다.

Initialized empty Git repository in .git/


(.git/ 디렉토리에 빈 Git 리파지토리가 초기화 되었습니다)
즉, 작업 디렉토리를 초기화 한 것이다. ".git"라는 디렉토리가 생성된 것을 볼 수 있을 것이다.
다음은 git-add를 사용해서 현재 디렉토리(.)의 모든 내용을 스넵샷으로 저장한다.

$git add .


현재의 스넵샷이  "index"라고 하는 임시 스테이지 영역에 저장되었다. 이제 git-commit을 이용해서 index의 내용을 리파지토리에 영구 저장할 수 있다.

$git commit


이를 실행하면 커밋 메세지 입력을 요구할 것이다. 이렇게 프로젝트의 첫 벗째 버젼을 git에 저장하였다.

변경을 가하기


몇몇 파일을 수정하고, index에 저장하려면 아래와 같이 한다.

$git add file1 file2 file3


이제 커밋을 할 수 있는 상태가 되었다. 이 때 git-diff명령에 --cached 옵션을 사용해서 commit할 내용의 미리보기를 해 보자.
 
$git diff -cached


(--cached 옵션을 지정하지 않으면, 현재 디렉토리와 index사이의 차이점이 표시된다.)

git-status 명령을 사용해서 현재상태를 확인할 수 있다.

$git status
# On branch master
# Changes to be committed:
#   (user "get reset HEAD <file>..." to unstage)
#
#        modified:   file1
#        modified:   file2
#        modified:   file3
#


더 수정할 것이 있다면 수정을 하고, 그 내용을 index에 적용한 다음에 마지막으로 커밋을 한다.

$git commit


마찬가지로 커밋에 대한 간단한 설명을 입력하는 프롬프트가 표시될 것이다.
git-add 를 생략할 수 있는 방법도 있다.

$git commit -a


를 실행하면 수정된 파일들을 자동으로 인식해서 index에 넣고 커밋까지 한번에 해준다. 단 새로 생성된 파일은 자동으로 add되지 않는다.

커밋 메세지에 대해서: 필수로 입력해야 하는 것은 아니지만, 첫 번째 줄에 수정 내용에 대한 전체적인 요약을 50문자 이내로 기술하고 한 줄을 띄우고 상세한 내용을 기술하는 것이 좋다. 예를 들어, 커밋의 내용을 email로 보내 주는 툴들은 첫 번째 라인을 제목으로 사용하고 나머지를 본문으로 사용한다.

Git은 파일자체를 트렉킹하는 것이 아니고 내용을 트렉킹한다.


많은 버젼관리 시스템들은 새로운 파일을 버젼관리에 추가하기 위해 add 명령을 사용한다. 반면에 git의 add명령은 더 심플하면서 좀 더 강한 기능을 가진다. git-add는 새로운 파일의 추가에도 사용하지만 이미 있는 파일들중에서 수정된 파일의 내용을 index에 저장할 때도 사용된다 (영어로 스냅샷을 스테이징한다라고 표현). commit을 하면 이 index에 저장된 스냅샷이 commit되게 된다.

프로젝트 히스토리 보기

변경 내용의 히스토리를 열람하려면

$git log


명령을 사용한다. 모든 스텝별 변경사항을 보려면

$git log -p


를 실행한다.

때로는 요약한 내용으로 보는 것이 편할 때가 있다. 이럴 때 아래와 같이 실행한다.

$git log --stat --summary

브랜치 관리하기


하나의 git 리파지포리에 복수 개의 브랜치를 유지할 수 있다. 새로운 브랜치 "experimental"을 생성하려면

$git branch experimental


이라고 실행한다. 이제

$git branch


라고 실행하면 아래와 같이 브란치의 리스트를 표시할 것이다.

  experimental
* master


"experimental" 브랜치가 방금 생성한 브랜치이다. "master" 브랜치는 처음에 자동으로 생성되는 기본 브란치이다. * 마크는 현재 사용중인 브랜치를 가리킨다.

$git checkout experiment


라고 실행해서 experimental 브랜치로 스위치할 수 있다. 파일을 수정해서 커밋을 한 다음 master 브랜치로 되돌아가 보자.

(파일 수정)
$git commit -a
$git checkout master


방금 수정했던 내용들이 다 사라졌을 것이다. 왜냐하면 experimental 브랜치에 수정을 커밋한 다음에 master 브랜치로 스위치 해서 돌아 왔기 때문이다.
master에도 수정을 가해보자.

(파일 수정)
$git commit -a


이제 두개의 브랜치는 각각 서로 다른 수정분들을 가지고 있다. experimental에 가했던 수정을 master로 병합하려면 아래와 같이 실행한다.

$git merge experimental


각 수정본들이 충돌하지 않았다면 이제 커밋을 할 수 있는 상태가 된다(병합 결과를 master에 커밋할 수 있는 상태). 만약 충돌하는 부분이 있을 경우, 해당 파일내에 충돌내용을 표기하는 마크가 남을 것이다.

$git diff


명령으로 그 마크를 확인해 볼 수 있다. 충돌한 부분을 해결했다면 아래와 같이

$git commit -a


실행해서 병합 + 충돌 해결분을 커밋해서 병합을 완료한다. 마지막으로

$gitk


를 실행하면 히스토리를 멋있는 그래픽으로 표시할 것이다.
이제 experimental은 삭제해도 된다.

$git branch -d experimental


아직 병합이 완료가 되지 않은 경우에는 삭제할 수 없다. 만약 강제로 삭제하고 싶다면 아래의 예와 같이 -D 옵션을 사용한다.

$git branch -D crazy-idea


브랜치는 부하도 적고 사용하기도 쉽기 때문에 무언가를 실험해 보고 싶은 경우 좋은 방법이 된다.

git으로 협업하기


Alice가 /home/alice/project 에 git 리파지토리가 있는 상태로 새로운 프로젝트를 시작했고, 그리고 같은 머신에 홈디렉토리를 가지는 Bob이 도움을 주고 싶어한다고 하자.

Bob은

bob$ git clone /home/alice/project myrepo


라는 명령으로 myrepo라는 새로운 디렉토리에 Alice의 리파지토리를 복제해 온다. clone은 원래의 프로젝트의 히스토리까지 모두 자신의 것으로 복제해 온다.
Bob은 이제 파일들을 수정하고 커밋한다.

(파일 수정)
bob$git commit -a
(필요에 따라서 반복)


작업이 끝난 때에 Bob은 Allice에게 /home/bob/myrepo로 부터 수정내용을 가지고 가라고 한다. Alice는 아래와 같이 실행한다.

alice$cd /home/alice/project
alice$git pull /home/bob/myrepo master


이 명령은 Bob의 master 브랜치를 Alice의 현재 브랜치에 병합한다. 이 사이에 Alice가 내용을 수정해서 충돌이 발생했다면 충돌한 부분들을 직접 고쳐야할 것이다.

pull 명령은 2가지의 일을 한다. 원격의 브랜치로 부터 변경된 내용을 가지고 오기(fetch)와 현재 브랜치에 병합하기(merge)이다.

일반적으로 Alice는 pull을 하기 전에 현재의 변경내용을 커밋을 할 것이다. 만약 Bob의 작업내용이 Alice의 작업과 충돌한 경우 Alice는 현재의 작업디렉토리와 index를 사용해서 충돌을 해결하려고 할 것이지만 커밋하기 전의 변경내용들과 섞여버려서 방해가 될 것이기 때문이다. (실제로 git은 내용은 fetch해 오지만 merge는 실패하게 된다. 이런 경우 Alice는 어떤 방법으로라도 작업 디렉토리에 가했던 변경내용을 없애고 다시 pull을 실행해야 할 것이다.)  

Alice는 fetch 명령을 사용해서 실제로 Bob의 작업 내용을 merge하기 전에 확인해 볼 수 있다. 이를 위해  FETCH_HEAD라는 심볼을 사용한다.

alice$ git fetch /home/bob/myrepo master
alice$ git log -p HEAD..FETCH_HEAD


fetch 명령은 로컬에 아직 commit되지 않은 내용이 있더라도 안전하다. HEAD..FETCH_HEAD 범위 지정 표기는 FETCH_HEAD까지의 경위에서 HEAD까지의 경위을 뺀 범위를 의미한다. alice는 이미 HEAD까지의 경위를 알고 있고 Bob이 FETCH_HEAD의 상태가 되기까지의 경위(즉 아직 Alice의 시야에 없던 변화들)를 살펴보고 있는 것이다.

Alice는 Bob이 가한 변경을 시각적으로 확인해 보려면 아래와 같이 실행할 것이다.

alice$ gitk HEAD..FETCH_HEAD


git log에서 보았던 ..(점 둘) 범위 지정 표기를 사용하고 있다.
Alice는 Bob의 변경 내용 뿐만 아니라 자신의 변경 내용까지 한 번에 확인하고 싶을 때도 있을 것이다. 이럴 때 ..(점 둘)대신에 ...(점 셋)을 사용한다.

alice$ gitk HEAD...FETCH_HEAD


여기서 정의한 범위는 "각각의 경위를 모두 표시하되, 공통 경위는 제외하라"이다.
범위 지정 표기는 git log와 gitk 양쪽 모두에 사용할 수 있다는 점도 잊지 말자.

Bob이 변경한 내용을 살펴본 후 Alice는 급하게 pull하지 않아도 되겠다고 판단했다면 그냥 작업을 계속 진행한다. Bob이 변경한 내용에 지금 바로 필요한 내용이 있다면 현재 작업중인 내용을 잠시 피신(git-stash) 시키고 pull을 한 다음에 다시 작업하던 내용을 복귀(git-unstash) 시켜서 최신의 상태가 되도록 할 것이다.

작고 잘 짜여진 그룹에서 작업중이라면 특정 repository들과 반복해서 상호작용을 하게 될 것이다. 이런 repository를 간편하게 지정할 수 있도록 remote repository로 별명을 지을 수 있다.

alice$ git remote add bob /home/bob/myrepo


이제, Alice는 pull의 첫 부분인 git-fetch 명령을 아래와 같이 실행할 수 있다.

alice$git fetch bob


길게 적어서 fetch해 왔던 경우와는 다르게,  git-remote 명령으로 정했던 별명으로를 fetch를 실행한 경우, fetch해 온 내용은 remote 트랙킹 브랜치에 저장된다. 이 경우에는 bob/master 가 된다. 아래와 같이 실행하면

alice$git log -p master..bob/master


Alice의 master 브랜치로 부터 브랜치해 간 이래 변경된 내용을 보여주게 된다.
내용을 확인 했다면 Alice는 아래와 같은 명령으로 merge를 실행할 것이다.

alice$git merge bob/master


merge 명령 대신에 Alice의 remote 트랙킹 브랜치로 부터 pull해오는 방법도 있다.

alice$git pull . remotes/bob/master


git pull은 항상 현재의 브랜치로 merge하게 된다.
나중에, Bob은 아래의 명령으로 Alice의 변경내용을 pull해 온다.

bob$git pull


Bob은 굳이 Alice의 repository를 명시하지 않아도 됨에 주의한다. Bob이 Alice의 repository를 clone했을 때 git은 그녀의 repository 위치를 repository 설정에 저장해 두었기 때문이다.

bob$git config --get remote.origin.url
/home/alice/project


git-clone에 의해서 생성된 설정 내용은 git config -l 명령으로 확인 가능하다. git-config man 페이지를 참조하면 각 옵션들의 의미를 알아 볼 수 있다.

git은 당시의 Alice의 master 브랜치를 origin/master라는 이름으로 저장해 둔다.

bob$ git branch -r
origine/master


이후, Bob이 다른 호스트에서 작업하게 되었다면 ssh를 통해서 여전히 clone이나 pull이 가능하다.

bob$ git clone alice.org:/home/alice/project myrepo


이외에도 git용 프로토콜이나 rsync, http 등도 사용할 수 있다. 자세한 내용은 git-pull을 참고한다.
git은 CVS유사모드로도 사용할 수도 있다. 이 모드는 중앙 repository를 두고 여러 유저가 변경을 push한다. 자세한 내용은 git-push나 gitcvs-migration을 참고한다.

내력조회하기


git 내력이란 밀접한 commit들의 연속들을 의미한다. 앞에서 이미 git-log를 명령으로 commit들의 일람을 확인해 보았었다. 각 엔트리의 첫번째 라인은 커밋의 이름임을 주목하자.

$git log
commit c82a22c39cbc32576f64f5c6b3f24b99ea8149c7
Author: Junio C Hamano <junkio@cox.net>
Date:   Tue May 16 17:18:22 2006 -0700

merge-base: Clarify the comments on post processing


이 이름으로 git-show명령을 사용해서 해당 commit의 상세를 볼 수 있다.

$git show c82a22c39cbc32576f64f5c6b3f24b99ea8149c7


commit을 지정하는 방법은 이름말고도 더 있다. 이름의 앞 부분만 지정해도 (중복되지 않을 만큼 긴) 된다.

$git show c82a22c39c     #이름의 앞부분의 어느정도만 적어도 된다.
$git show HEAD                #현재 브랜치의 마지막
$git show experimental    #experimental 브랜치


모든 commit은 일반적으로 하나의 parent commit을 가진다. parent commit이란 프로젝트의 바로 앞의 상태를 가리킨다.

$git show HEAD^      #HEAD의 parent
$git show HEAD^^    #HEAD의 parent의 parent
$git show HEAD~4   #HEAD로 부터 4단계위의 parent


merge commit의 경우에는 하나 이상의 parent를 가질 수도 있다.

$git show HEAD^1     #HEAD의 첫번째 상위 parent(HEAD^와 동일)
$git show HEAD^2     #HEAD의 두번째 상위 parent


commit명에 별칭을 붙이는 것도 가능하다. 아래와 같이 실행하면

$git tag v2.5 1b2e1d63ff


1b2e1d63ff 대신에 v2.5라는 이름을 사용할 수 있게 된다. 이 이름을 타인과도 공유 하려면 (예를 들어, 릴리즈 번호라던지) tag 객체를 생성해야 하고, sign할 필요가 있을 수도 있다. 자세한 내용은 git-tag를 참고한다.

commit명을 인수로 받는 모든 git 명령에 이 이름을 사용할 수 있다.

$git diff v2.5 HEAD            #현재 HEAD와 v2.5와 비교
$git branch stable v2.5     #v2.5로부터 새로운 브랜치 "stable"을 브랜치한다.
$git reset --hard HEAD^   #현재 브랜치와 작업 디렉토리를 HEAD^상태로 되돌린다.


마지막 명령문은 주의해야 한다. 작업디렉토리의 변경내용뿐만 아니라 HEAD^이후의 모든 commit들까지 리셋된다. 리셋된 commit들이 다른 브랜치에도 존재하지 않다면 완전히 소실되는 것이 된다. 또한 다른 개발자들이 사용하고 있는 공개 브랜치를 reset하지 않도록 한다. reset할 경우 merge한 내용들까지 history에서 사라지게 되는 결과를 낳게 된다. 만약 push했던 내용을 되돌리고 싶다면 git-reset 대신에 git-revert를 사용하도록 한다.

git-grep 명령은 프로젝트의 모든 버젼으로부터 문자열 검색을 할 때 사용한다.

$git grep "hello" v2.5


는 v2.5에서 hello라는 문자열을 검색한다.
commit명을 생략하면 현재 디렉토리의 모든 파일에서 검색을 실행할 것이다.

git grep "hello"


는 git이 현재 tracking하고 있는 파일들에서 검색을 실행한다.
git의 많은 명령들을 commit들을 인수로 받는데 commit들을 지정할 수 있는 여러 방법이 있다. 여기 git-log의 예제가 있다.

$git log v2.5..v2.6      #v2.5와 v2.6사이의 commit들
$git log v2.5..              #v2.5이래의 모든 commit들
$git log --since="2 weeks ago"    #2주 전 이래의 모든 commit들
$git log v2.5.. Makefile       #v2.5이래에 Makefile에 변경을 가했던 모든 commit들


git-log에 지정할 수 있는 commit의 범위의 시작과 끝은 상속관계에 관계없이 지정할 수도 있다. 예를 들면, "stable-release"와 "master"라는 브랜치가 예전에 분기되었다고 하면

$git log stable..experimental


는 experimental 브랜치에는 존재하고 stable 브랜치에는 존재하지 않는 commit들을 출력한다. 반면에

$git log experimental..stable


는 stable 브랜치에는 존재하고 experimental에는 존재하지 않는 commit들을 출력한다.

git-log 명령에는 약점도 존재한다. commit의 이력을 일람으로만 출력한다는 점이다. 만약 이력에 분기되었다가 다시 merge되었다면 git-log가 출력하는 commit들의 순서는 의미가 없어지게 된다.

복수의 공헌자들이 작업하는 대부분의 프로젝트들(리눅스 커널등)은 많은 merge를 가지고 있다. 이럴 경우 gitk는 이력을 시각화하는데 더 유용할 것이다. 예를 들면

$gitk --since="2 weeks age" drivers/


는 최근 2주간의 drivers 디렉토리에 가한 commit들을 열람할 수 있게 한다. (주: gitk의 폰트 크기를 조절하려면 컨트롤키를 누를 상태에서 +나 -키를 누른다)

마지막으로, 파일명을 인수로 받는 git 명령의 경우, 파일명 앞에 commit명을 지정할 수 있다.

$git diff v2.5:Makefile HEAD:Makefile.in


는 v2.5의 Makefile과 HEAD의 Makefile.in의 차이를 출력한다.
마찬가지로 git-show라면

$git show v2.5:Makefile


라는 식으로 사용가능하다.

다음 단계


이 튜토리얼만으로도 프로젝트의 분산 리비젼관리를 하는 것에는 충분할 것이다. git의 깊이와 힘을 완전히 이해하기 위해서는 2개의 간단한 기본적인 원리에 대해서 이해해야 한다.

- 객체 데이터베이스  : 프로젝트( 파일, 디렉토리, commit들)의 이력을 저장하는 시스템
- index 파일 : 디렉토리의 상태를 캐싱하는 파일. commit을 생성하거나 작업 디렉토리를 check out하거나 merge에 관련된 여러 트리를 저장하는 곳

이 튜토리얼의 Part 2에서는 객체 데이터베이스와 index 파일과 그외 여러가지들에 대해서 설명한다.
Part 2로 바로 진행하고 싶지 않다면 여기 흥미로운 읽을만한 것들이 있다.

git-format-patch(1), git-am(1): These convert series of git commits into emailed patches, and vice versa, useful for projects such as the Linux kernel which rely heavily on emailed patches.

git-bisect(1): When there is a regression in your project, one way to track down the bug is by searching through the history to find the exact commit that's to blame. Git bisect can help you perform a binary search for that commit. It is smart enough to perform a close-to-optimal search even in the case of complex non-linear history with lots of merged branches.

gitworkflows(7): Gives an overview of recommended workflows.

Everyday GIT with 20 Commands Or So

gitcvs-migration(7): Git for CVS users.


SEE ALSO


원문: http://www.kernel.org/pub/software/scm/git/docs/gittutorial.html
번역: 허 련호(airless@funit.net)
2009/04 생성, 2011/08 수정
크리에이티브 커먼즈 라이센스
Creative Commons License
2011/09/01 18:11 2011/09/01 18:11
TAG
받은 트랙백이 없고, 댓글이 없습니다.

댓글+트랙백 RSS :: http://blog.hwport.com/rss/response/634

댓글+트랙백 ATOM :: http://blog.hwport.com/atom/response/634


본 내용은 "http://www.imaso.co.kr/?doc=bbs/gnuboard.php&bo_table=article&wr_id=35970" 에서 발췌한 것임을 밝힙니다.



NDIS 드아리버는 보안을 공부하는 사람에게 아주 중요하다. NDIS 드라이버를 이용해 네트워크 패킷을 확인할 수 있기 때문이다. WinPcap을 이용해서도 패킷을 볼 수 있지만 내부 로직을 알기위해서는 NDIS 드라이버의 개념이 꼭 필요핟. 이번 컬럼에서는 NDIS 드라이버에 대한 개론과 패킷을 제어하기 위한 NDIS Intermediate 드라이버와 윈도우 드라이버 개발 환경구축에 대해 알아보도록 하겠다.

정경호(moltak@gmail.net), 전종오(ringopow@ lycos.co.kr)|필자들은 현재 광주 삼성소프트웨어 멤버십 회원이다. 작년에 처음 만났지만 서로 잘 통해서 현재까지 프로젝트를 계속 같이 수행하고 있다. 필자들은 보안, Windows Driver 개발에 관심이 많고 2010 마이크로소프트에서 주관하는 Imagine Cup Embedded 분야에서 “Poom” 이란 프로젝트로 국내 선발전에서 아쉽게 2위를 수상하였다.

우리가 사용하고 있는 네트워크 환경은 상호간의 신뢰를 기반으로 많은 양의 정보를 주고받고 있다. 그러나, 최근 이러한 네트워크 환경은 악의적인 사용자에 의해 다양한 방법으로 공격받고 있다.

대표적인 공격 방식은 보안에 취약한 시스템을 통해 악성코드가 유입되는 경우나, 사용자가 웹 사이트에 접속하는 것만으로도 자동으로 악성코드가 다운로드되는 경우 등 다양한 형태로 나타나고 있다.

지난 2009년 7월 7일부터 10일까지 4일에 걸쳐 발생된 일명 ‘7.7 DDoS 대란’은 청와대를 비롯해 주요 정부기관 홈페이지와 대형 포털사이트, 금융권, 쇼핑몰 등 국내의 주요 홈페이지들이 타깃이 되어 벌어진 대표적인 보안 사고였으며, 앞서 설명한 공격 방식의 대표적인 예다.

이처럼 우리는 DDoS 대란으로 인해 큰 혼란이 빠졌었고, 1년여가 지난 현재까지도 언젠가 다시 제 2, 제 3의 대란이 일어날 지도 모른다는 불안에 떨고있다.  

<화면 1> DDoS 공격의 개요

<화면 1>으로 본 7.7 DDoS 공격은 수많은 PC사용자들이 자신도 모르는 사이에 악성코드에 감염되어 좀비PC로 활동하면서, 다른 사이트를 공격하고 있는 것을 볼 수 있다.

좀비 PC들의 DDoS 공격으로 인해 1차에 26개, 2차에 16개, 3차에 7개 사이트를 마비시켰으며, 좀비 PC가 된 개인 사용자의 PC또한 자폭코드로 인해 HDD에 저장된 개인 데이터 또한 손상되는 결과를 가져왔다.
7.7 DDoS대란 당시의 피해액은 수백억원에 달하며, 그로인해 네트워크 보안은 더욱 중요시 되어가고 있다. 또한 현재까지도 제 2의 DDoS대란 방지를 위해 다양한 연구가 활발하게 진행되고 있다. 지금부터 설명하게 될 내용 또한 이를 위한 다양한 연구방법 중 하나다.

본 컬럼에서는 PC 사용자의 대부분이 이용하는 OS인 윈도우에서 NDIS를 이용한 패킷분석과 이를 관리할 수 있는 개발법에 대해 알아보도록 하겠다.

NDIS란 무엇인가
NDIS(Network Driver Interface Specification)는 MS와 쓰리콤(3COM)이 단일 NIC(Network Interface Card : 우리가 사용하는 랜카드 디바이스)상에서 여러 가지의 프로트콜 스택을 동시에 지원하기 위해 만든 계층형 네트워크로 NIC 드라이버가 하층의 네트워크 인터페이스 카드, 상위층의 프로토콜 드라이버, OS와 통신하기 위한 인터페이스를 정의하고 있다.

즉, NDIS NIC 드라이버를 이용할 경우 NDIS를 통해 하위의 NIC와 링크시키는 상위의 드라이버를 무수히 지원할 수 있으므로 일종의 라이브러리로 생각할 수 있다.

NDIS Driver를 개발하기 위해서는 WDM에 대한 기본지식이 필요한데, 이는 NDIS Driver도 WDM의 한 종류이기 때문이다.

많은 독자들이 OSI 7계층에 대해 들어보았을 것이다. OSI 7계층은 표준화 기구인 ISO에서 제시한 네트워크 모델이다. OSI는 네트워크 구성에 대한 권고안으로 OSI 참조 모델을 따르는 네트워크는 서로 다른 기술로 구성된 네트워크라 할지라도 통신을 가능하게 할 수 있다. 윈도우 또한 OSI 참조 모델을 따르고 있는데, NDIS 구조를 알기 위해서는 윈도우의 네트워크 계층 대한 이해가 필요하다.

윈도우는 <그림 1>과 같이 OSI 7계층을 따르는 네트워크 계층 가진다. NDIS는 데이터 링크 계층에 존재하는 것을 알 수 있다. NDIS는 윈도우상에 Ndis.sys로 정의되어 있으며, Ndis.sys가 로딩되지 않을 경우에는 네트워크를 사용할 수 없다. 각각의 프로토콜 드라이버 또한 Tcpip.sys 등과 같은 시스템 파일로 정의되어 있는 것을 알 수 있다.

<그림 1> 윈도우 네트워크 레이어

NDIS를 개발할 때는 개발하는 운영체제에 따라 다르다. 현재 최신 버전은 윈도우비스타 용으로 나온 6.0이며, 윈도우XP 의 경우에는 5.x 버전을 이용해 개발한다.

각 버전에 따른 사항은 http://www.microsoft.com/hwdev를 통해 살펴볼 수 있다.

NDIS 아키텍처
<그림 2>에서 나타났듯이, NDIS는 총 3개 계층으로 구성되며 가장 하단에는 NIC를 제어하는 미니포트 드라이버와 TCP/IP, IPX 등의 프로토콜을 구현해 놓은 프로토콜 드라이버, 그리고 그 사이에 존재하는 인터미디엇 드라이버로 구성된다.

<그림 2> NDIS 아키텍처

각각의 드라이버에 대해서는 이후에 더 자세히 다루겠지만, 간단히 설명하면 미니포트 드라이버는 NIC 개수마다 존재하며 직접 하드웨어를 제어하는 역할을 수행하고 물리계층을 관리하는 역할을 맡는다.

프로토콜 드라이버는 하드웨어와 독립적으로 구현할 수 있으며, NDIS 인터페이스를 통해 하위의 미니포트 드라이버나 인터미디엇 드라이버와 통신이 이뤄진다. 또한 상위의 TDI 계층과 연결되어 있으며 TCP/IP 등의 프로토콜 등이 정의되어 있다. 또한 프로토콜 드라이버는 임의로 개발자에 의해 추가될 수 있는데, 추가 시에는 기존의 프로토콜 드라이버를 대체하는 것이 아니라 병렬적으로 추가된다.

인터미디엇 드라이버는 필수적인 요소가 아니라 개발자가 독립적으로 추가할 수 있다. 프로토콜 드라이버와 미니포트 드라이버 사이에 위치함으로써 패킷을 임의로 가공할 수 있으므로 주로 필터링 역할을 수행한다.

NDIS를 이용한 네트워크 모니터링 방법
TDI 계층과 NDIS 계층은 커널모드로 동작한다. 커널모드에서 네트워크를 모니터링하기 위해서는 패킷 캡처가 필수적이다. 또한 이를 위해서6 TDI 계층에서 TDI 필터를 개발하거나 NDIS 프로토콜 드라이버를 개발하는 방법이 있다.

우선 TDI 필터를 개발할 경우에는 패킷 캡처를 통해 IP, Port, 애플리케이션 프로토콜(TCP 이후의 데이터)를 얻을 수 있으며 모니터링, 변조, 차단 등의 역할을 수행할 수 있다. 그러나 TDI Filter는 커널모드 최상위에 존재하기 때문에 한계가 발생하기도 한다.

또 다른 방법으로는 NDIS 프로토콜 드라이버를 개발할 경우 이더넷 헤더, IP 헤더, TCP 헤더, 애플리케이션 프로토콜까지 모든 로우 패킷에 대한 정보를 얻을 수 있으므로 보다 자세한 모니터링이 가능하다. 모니터링이 가능한 이유는 수신시에 미니포트 드라이버가 상위의 모든 프로토콜 드라이버에게 수신된 패킷을 전송해 주기 때문이다.

그러나 직접적인 변조 및 차단은 할 수 없다. 그 이유는 NDIS 프로토콜 드라이버를 개발할 경우 기존의 TCP/IP 프로토콜 드라이버를 대체하는 것이 아니라, 수평적으로 추가되는 형식으로 이뤄지기 때문이다.

즉, 수신되는 패킷은 NDIS 인터페이스를 통해 얻어올 수 있으나, 송신되는 패킷은 기존의 TCP/IP 프로토콜 드라이버를 통해서 미니포트 드라이버에 전송이 되므로 다른 프로토콜 드라이버가 이를 차단할 수 없다.

<그림 3> 프로토콜 드라이버 패킷 흐름

<그림 3>에서 아래쪽을 향한 화살표는 송신 패킷의 흐름이고, 위로 향하는 화살표는 수신 패킷의 흐름이다. 프로토콜 드라이버 계층을 보면 기존 MS에서 구성해 놓은 TCP/IP 프로토콜 드라이버와 IPX/SPX 등 기존 프로토콜 드라이버가 존재함을 알 수 있다. 개발자가 임의로 유저(User)라는 프로토콜 드라이버를 추가할 경우 <그림 3>과 같이 추가된다.

상위계층에서 TCP/IP 프로토콜로 송신패킷이 발송되면 TCP/IP 프로토콜 드라이버를 통해 직접 미니포트 드라이버에 전달이 되고 미니포트 드라이버가 NIC로 전송하는 것을 볼 수 있다. 그렇기 때문에 프로토콜 드라이버에서는 모니터링만 가능하고 전송되는 패킷의 가공이나 필터링을 수행하기 힘들다.

패킷이 수신되는 경우는 NIC에서 수신되는 패킷이 있을 때인데, 미니포트 드라이버가 이를 받아서 상위 계층의 모든 프로토콜 드라이버에 전송하기 때문에 개발자가 임의로 추가한 유저라는 이름의 유저라는 프로토콜 드라이버에도 수신된 패킷을 받을 수 있는 것을 볼 수 있다. 우리가 흔히 사용하는 WinPcap은 프로토콜 드라이버를 사용한 경우이다.

네트워크를 관리하기 위해서는 모니터링뿐만 아니라, 송신되는 패킷에 대해서도 제어가 가능해야 하므로 이를 위해서는 NDIS 인터미디엇 드라이버를 개발해야 한다.

인터미디엇 드라이버는 프로토콜 드라이버와 다르게 병렬적으로 추가되는 것이 아니라 미니포트 드라이버와 프로토콜 드라이버의 사이에 위치하기 때문에 송·수신 패킷에 대한 제어가 가능하다.

또 다른 방법은 NDIS 훅 드라이버를 개발하는 방법이다. NDIS 훅 드라이버를 개발과 사용성의 편의로 주로 이용되긴 하나, 윈도우비스타 64bit에서는 패치로 인해 사용할 수 없는 단점이 있다.

훅 드라이버는 NDIS에서 미니포트 드라이버와 프로토콜 드라이버와의 연결을 나타내는 OPEN_BLOCK 이라는 부분이 있는데 이곳에 있는 송·수신 포인터를 후킹하는 것이 NDIS 훅 드라이버다.

NDIS 미니포트 드라이버
미니포트 드라이버는 NDIS 계층에서 가장 최하위에 위치하는 일반적인 이더넷 드라이버로서, NIC를 통해 데이터를 보내고 받는 것을 포함해 NIC를 직접 제어하는 역할을 수행한다.

상위계층의 인터미디엇 드라이버나 프로토콜 드라이버와 인터페이스를 수행하기도 하는데, 특히 이더넷 드라이버이기 때문에 TCP/IP 같은 것은 알 수가 없고, 여기서 볼 수 있는 것은 오직 데이터링크(MAC)과 물리(PHY)계층 뿐이라는 점이 특징이다.

다만 요즘에는 성능 향상을 위해서 TCP 패킷이나 IP 패킷의 체크섬 오프로딩, TCP 세그멘테이션 등을 하드웨어 수준에서 지원하는 경우가 많다.

미니포트 드라이버를 개발하기 위해서는 많은 어려움이 따른다. 일단 개발시에는 하드웨어를 직접 제어해야 하기 때문에 개발할 이더넷 카드에 대한 스펙에 대한 고려가 충분히 이뤄져야 하며, 개발에 대한 자료 또한 많이 부족하기 때문에 WDK를 설치하는데, 이때 NDIS 예제 코드가 같이 설치된다. 때문에 필자는 이 예제를 참조하는 것이 가장 좋다고 추천하고싶다.

미니포트 드라이버의 주요기능으로는 NIC의 초기화와 등록, NIC에 전송할 패킷 전달, NIC에서 수신되는 패킷을 상위 계층인 인터미디엇 드라이버나 프로토콜 드라이버로의 전달, NIC의 제어(어댑터 설정 변경, 종료, 리셋 등)이다.

NDIS는 윈도우네트워크 드라이버 전용으로 만들어진 프레임워크라고 볼 수 있기 때문에 콜백을 잘 맞추기만 하면 된다. 콜백의 기능, 전체적인 콜백 호출 흐름, 각 콜백 안에서 호출해야 할 API들을 파악하는 것이 가장 중요한 부분이다.

미니포트 드라이버는 초기화 작업 NdisMInitializeWrapper를 호출하여 NDIS의 핸들을 얻는 것으로 시작된다.
여기서는 인터미디엇 드라이버에 대한 내용을 다루기 때문에 간단하게 콜백에 대한 소개만 하고 넘어가겠다. 각 콜백의 자세한 내용은 MSDN을 참고하자.

·MiniportInitialize : 네트워크 어댑터가 활성화 되는 경우 호출된다.
·MiniportQuertInformation : 네트워크 어댑터가 초기화 된 직후부터 네트워크 어댑터의 정보를 조회하는 경우 호출된다.
·MiniportSetInformation : 네트워크, 어댑터의 설정값을 조작하는 경우 호출된다.
·MiniportCheckForHang : NDIS에서는 미니포트 드라이버가 정상동작을 하는 것을 체크하는 경우 호출
·MiniportReset : MiniportCheckForHang에서 체크 시 정상 동작을 하지 않을 경우 호출되어 미니포트 어댑터를 리셋한다.
·MiniportReturnPacket : 상위 계층으로 전송한 패킷 버퍼가 이 콜백을 통해 되돌아온다.
·MiniportSendPackets : 상위 계층의 패킷 전송 요청이 있을 경우 호출된다.
·MiniportIsr : 인터럽트 서비스 루틴에서 예약된 작업을 처리할 경우 호출된다.

미니포트 드라이버에서 패킷을 송·수신 할 경우에는 다음과 같은 작업이 수행된다.

Packet 전송
  - 전송계층 드라이버가 전송할 패킷 발생
  - 전송계층 드라이버가 NDIS 라이브러리의 NdisXxx 함수 호출
  - NDIS에 의해 미니포트에서 노출된 적당한 MiniportXxx함수 호출
  - 미니 포트에 패킷 전달
  - 미니 포트 드라이버에서 적당한 NdisXxx 함수 호출
  - NIC에 패킷 포워드

Packet Indicate
  - NIC에서 주소화된 패킷 수신
  - 하드웨어 인터럽트 발생
  - NDIS에서 적당한 MiniportXxx 함수 호출(MiniportSend)
  - 미니포트에 인터럽트 전달
  - 미니포트에서 데이터 전송 설정
  - NdisXxx 함수를 호출하여 바인드된 상위 레벨 드라이버로 수신된 패킷을 인디케이트

인터미디엇 드라이버를 개발하기 위해서는 미니포트 드라이버에 대한 이해도 필요하다. 그 이유는 인터미디엇 드라이버의 Upper Edge가 프로토콜 드라이버에서는 미니포트 역할을 수행하기 때문이다.

NDIS 인터미디엇 드라이버
인터미디엇 드라이버는 미니포트 드라이버와 프로토콜 드라이버 중간에 위치하며, 하위 계층과 상위 계층과 통신을 한다. 필수적인 계층은 아니기 때문에 개발자에 의해 추가될 수 있다.

인터미디엇 드라이버의 주요 기능은 크게 3가지다. 첫째, 다른 네트워크 미디어 사이를 변환하는 작업을 수행한다. 이더넷과 ATM과 같은 서로 다른 전송계층의 패킷을 매핑하는 기능을 수행함으로써 변환 작업을 한다. 예를 들어 NIC가 ATM용이고 프로토콜 드라이버가 이더넷 전송계층을 사용할 경우에는 인터미더엇 드라이버를 추가함으로써 변환 작업을 할 수 있다.

둘째, 패킷 필터링이나 블록킹 기능을 수행한다. 미니포트 드라이버로부터 수신되는 패킷의 정보와 프로토콜 드라이버에서 전달되는 패킷을 우선적으로 읽을 수 있으므로, 관리자의 요구에 따라 패킷을 관리할 수 있다.

셋째, 로드 밸런싱 기능을 수행한다. 예를 들어 하나의 가상 어댑터를 상위 레벨에 노출하여 하나 이상의 NIC로 전송 패킷을 분산 시키는 기능을 할 수 있다.

<그림 4> 인터미디엇 드라이버 구조

<그림 4>에서 보듯이 인터미디엇 드라이버는 크게 Upper Edge와 Lower Edge로 구성된다. Upper Edge는 프로토콜 드라이버인 전송계층 드라이버와 연결이 되고, Lower Edge는 미니포트 드라이버와 연결이 된다. 즉, Upper Edge는 프로토콜 드라이버의 입장에서 보면, 가상의 미니포트 드라이버 역할을 하게 되고, Lower Edge는 미니포트 드라이버 입장에서 보면, 프로토콜 드라이버의 역할을 수행하게 되는 것이다. Upper Edge와 Lower Edge는 다음과 같이 구성된다.

Upper Edge
- 상위의 프로토콜 드라이버와 통신하기 위해 MiniportXxx 노출(Miniport DriverEntry)
- 프로토콜 드라이버는 Intermediate Driver를 미니포트 드라이버로 인식
- 수신 된 패킷을 프로토콜 드라이버(Protocol Driver)로 전달
- 실제 NIC를 관리 하지 않음
- 프로토콜 드라이버가 바인딩 할 수 있는 가상 어댑터 제공 (NIC 역할)

Lower Edge
- 하위의 Miniport Driver와 통신하기 위해 ProtocolXxx 노출(Protocol DriverEntry)
- 미니포트 드라이버는 인터미디엇 드라이버를 프로토콜 드라이버로 인식
- 미니포트 드라이버에서 수신 된 패킷을 우선적으로 수신

<그림 4>에서 보듯이 전송 계층의 하위 단과 연결되는 부분인 인터미디엇 드라이버의 상위 단은 MiniportXxx 를 노출하고 있고 미니포트 드라이버의 상위 단과 연결되는 하위 단에는 ProtocolXxx를 노출하고 있음을 볼 수 있다.

그렇기 때문에 인터미디엇 드라이버를 개발할 경우에는 미니포트 드라이버 뿐 만 아니라 프로토콜 드라이버의 구조 또한 알고 있어야 개발하는데 용이하다고 할 수 있다.

NDIS 프로토콜 드라이버
프로토콜 드라이버는 NDIS 계층에서 가장 위에 있는 드라이버로써 네트워크 프로토콜(TCP/IP, IPX/SPX)과 같은 프로토콜 스택을 실행하는 전송 계층 내 가장 낮은 레벨의 드라이버이다. 프로토콜 드라이버는 병렬적인 구조를 가지며, 임의로 추가 시 앞에서 설명한 봐와 같이 수평적으로 추가된다. 프로토콜 드라이버는 마이크로 소프트에서 기본적인 프로토콜에 대한 드라이버(TCPIP.sys 등과 같은)들을 제공하고 있다.

프로토콜 드라이버의 기능은 어플리케이션에서 전송되는 데이터를 패킷에 복사하고, NDIS 함수를 호출하여 하위 레벨의 드라이버로 전송하는 기능과 하위 레벨에서 수신된 패킷을 받기 위해 프로토콜 인터페이스를 제공하고 수신된 데이터를 적당한 클라이언트 어플리케이션에 전송하는 기능을 수행한다.
프로토콜 드라이버 또한 Upper Edge와 Lower Edge로 구성되어 있다.

Upper Edge
 - Intermediate Driver와 Miniport Driver와의 인터페이스 수행
 - 패킷 전송 : NdisXxx 함수를 호출
 - 패킷 수신 : Protocol DriverEntry노출 (ProtocolXxx)
 - 하위 레벨의 드라이버에 유지된 정보를 읽고 설정하고 OS 서비스를 사용

Lower Edge
 - 하위의 Miniport Driver와 통신하기 위해 ProtocolXxx 노출(Protocol DriverEntry)
 - 미니포트 드라이버는 인터미디엇 드라이버를 프로토콜 드라이버로 인식
 - 미니포트 드라이버에서 수신 된 패킷을 우선적으로 수신

NDIS 개발 환경 구축
NDIS 드라이버를 개발하기 위한 환경은 WDM이나 WDF의 개발환경을 구축하는 것과 같다. 앞서 설명했듯이 NDIS 드라이버는 WDM 드라이버의 한 종류이기 때문이다.

필자가 사용하는 개발 환경은 윈도우 XP를 기반으로 설명할 것이며, 아래 내용중 경로를 설정해야하는 모든 부분은 전부 공백이 포함되지 않는 경우여야 한다.

비주얼 스튜디오 2008과 WDK의 환경 구축
개발 환경을 구축하기 위해 필요한 가장 첫 번째 작업은 WDK를 다운받아서 설치하는 것이다. WDK는 현재 최신 버전이 7.x 버전이며 다음의 링크를 통해 다운로드받을 수 있다.

www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=36a2630f-5d56-43b5-b996-7633f2ec14ff

설치법은 다운받은 가상 이미지를 로드하여 인스톨하면 된다. WDK를 설치 한 후에는 ddkbuild.bat 파일이 필요하다.

<화면 2> ddkbuild.bat

이 파일은 www.hollistech.com/Resources/ddkbuild/ ddkbuild3_13.zip를 통해 다운 받을 수 있다. 파일을 다운 받은 후 메모장 등을 통해 ddkbuild.bat 파일을 열어보면 <화면 2>와 같이 구성되어 있다. XP일 경우에는 <화면 2>에 빨간 박스 부분의 환경 변수를 추가해 주어야 한다. 물론 윈도우에서 내컴퓨터(속성) → 고급 → 환경변수(시스템 변수)에 추가해줘도 상관이 없다.

다음으로 비주얼 스튜디오 2008를 실행시킨 후 프로젝트 생성을 아래의 순서에 따라 생성한다.

<화면 3> 비주얼 스튜디오 2008 설정 프로젝트 생성

확인을 선택한 후 디버그 구성 설정에서는 <화면 4>와 같이 ddkbuild.bat 파일을 설정한다.

<화면 4> 빌드 옵션 설정

<화면 4>는 ddkbuild.bat를 가지고 컴파일하기 위한 설정이다. 여기서 -XP는 XP환경이라는 뜻이다. checked 옵션은 WDK 컴파일에는 디버그 모드와 릴리즈 모드가 있는데 checked 옵션을 사용할 경우에는 디버그 모드라는 뜻이다. -cZ 옵션은 컴파일 후 만들어진 부가적인 파일들을 모두 지우고 다시 빌드할 때 사용되는 옵션이다.

XP 환경이 아니거나 다른 빌드 옵션을 추가하고 싶을 경우에는 www.hollistech.com/Resources/ddkbuild/ddkbuildhelp3 _13.htm를 참조해 추가하면 된다.

프로젝트 생성이 완료된 후에는 도구 → 옵션 → 프로젝트 및 솔루션 → VC++ 디렉토리로 가서 <화면 5>,<화면 6>과 같이 설정한다. 이 설정은 설치한 WDK의 라이브러리를 이용하기 위한 설정이다.

<화면 5> WDK 헤더 파일 경로 설정

<화면 6> WDK 라이브러리 파일 경로 설정

여기까지 비주얼 스튜디오 2008에서 NDIS 드라이버 개발을 위해 설정할 수 있는 기본적인 환경구축에 대한 설명이 끝났다. NDIS는 다시 말하지만 WDK의 한 종류이기 때문에 WDK의 기본 구조를 모르는 상태에서는 개발할 수 없다. WDK에 대해서는 기본적으로 따로 공부를 해야한다는 점을 강조하고 싶다.

이번 컬럼에서는 NDIS 드라이버란 무엇인지와 각 드라이버들의 기능과 역할에 대해서 설명하였다. 다음 컬럼에서는 인터미디엇 드라이버를 어떻게 개발하는지에 대한 방법에 대해 설명할 예정이므로 독자들은 WDK에 대한 기본 지식을 습득하는 것이 따라오는데 큰 도움이 될 것이다.

크리에이티브 커먼즈 라이센스
Creative Commons License
2011/09/01 03:05 2011/09/01 03:05
받은 트랙백이 없고, 댓글이 없습니다.

댓글+트랙백 RSS :: http://blog.hwport.com/rss/response/633

댓글+트랙백 ATOM :: http://blog.hwport.com/atom/response/633


본 내용은 "http://blog.naver.com/lszeelee?Redirect=Log&logNo=110100215980" 에서 발췌한 것임을 밝힙니다.



UUID (Universally Unique ID)  란?

                                                UUID 자료(ITU-T) 링크 ~ http://www.itu.int/ITU-T/asn1/uuid.html





Universally Unique IDentifier (UUID)

From Wikipedia, the free encyclopedia
Jump to: navigation, search


A Universally Unique Identifier (UUID) is an identifier standard used in software construction, standardized by the Open Software Foundation (OSF) as part of the Distributed Computing Environment (DCE).


범세계적으로 고유한 식별아이디(UUID, Universally Unique Identifier)는 소프트웨어 개발에 사용되는 식별 아이디 표준의 일종으로, Didtributed Computing Envireonment (DCE)의 한 부분으로 OSF(Open Soft Foundation)에 의해 표준화되었다.


The intent of UUIDs is to enable distributed systems to uniquely identify information without significant central coordination.


UUID의 목적은 심각한 중앙집중식의 조정없이 분산 시스템에서 정보를 유일하게 식별할 수 있도록 하는데 있다


Thus, anyone can create a UUID and use it to identify something with reasonable confidence that the identifier will never be unintentionally used by anyone for anything else.


따라서 누군가 UUID를 생성하고 무언가를 식별하기 위해 생성된 UUID를 사용하는 경우,

그 동일한 UUID 식별자가 다른 누군가에 의해 다른 것을 식별하기 의도하지 않은 채 무심코 사용되는 일은 결코 없을 것이라는 점에 대해 꽤 신뢰할만(언제나 100%는 아니지만) 하다는 것이다


Information labeled with UUIDs can therefore be later combined into a single database without needing to resolve name conflicts.


그러므로 UUID로 식별되는 정보는 식별아이디가 서로 상충되는지 다시 확인하지 않고 나중에 하나의 Database 에 통합적으로 저장해서 활용할 수 있다는 것이다  


The most widespread use of this standard is in Microsoft's Globally Unique Identifiers (GUIDs).


UUID 표준 중에서 가장 널리 이용되는 것은  Microsoft#99 Globally Unique Identifiers(GUIDs)이다


Other significant uses include Linux's ext2/ext3 filesystem, LUKS encrypted partitions, GNOME, KDE, and Mac OS X, all of which use implementations derived from the uuid library found in the e2fsprogs package.


UUIDs are documented as part of ISO/IEC 11578:1996 "Information technology – Open Systems Interconnection – Remote Procedure Call (RPC)" and more recently in ITU-T Rec. X.667 | ISO/IEC 9834-8:2005. The IETF has published Standards Track RFC 4122 that is technically equivalent with ITU-T Rec. X.667 | ISO/IEC 9834-8.

Contents

[hide]
  • 1 Definition
  • 2 Implementations
  • 3 Random UUID probability of duplicates
  • 4 History
  • 5 See also
  • 6 References
  • 7 External links
  • Definition

    A UUID is a 16-byte (128-bit) number.


    UUID 는 16 byte(128 bit)로 이루어진 규격화된 숫자이다


    The number of theoretically possible UUIDs is therefore about 3 × 1038.


    따라서 이론적으로 가능한 UUID의 총 수는 3 x 10^38 이 된다


    In its canonical form,

     a UUID consists of 32 hexadecimal digits, displayed in 5 groups separated by hyphens, in the form 8-4-4-4-12 for a total of 36characters (32 digits and 4 hyphens).


    UUID 표준 형식을 살펴 보면

     

    UUID는 32 개의 hexadecimal(16진법) 글자로 이루어지는데, 모두 5 그룹으로 나뉘고 각 그룹 사이에는 하이펀으로 연결된다.

     

    첫번째 그룹: 8 글자(16진법 표기)

    두번째 그룹: 4 글자(16진법 표기)

    세번째 그룹: 4 글자(16진법 표기)

    네번째 그룹: 4 글자(16진법 표기)

    다섯째 그룹: 12 글자(16진법 표기)



    For example: 범세계적으로 고유한 식별 아이디 ~ UUID (예)

     

     

    UUID(예) : 550e8400-e29b-41d4-a716-446655440000

            (형식이 고정된 숫자) -> 컴퓨터에서 정보를 식별하기 위해 사용 가능하나 사람 중심의 유비쿼터스 서비스에는 부적합

     

    A UUID may also be used with a specific identifier intentionally used repeatedly to identify the same thing in different contexts.

     

    UUID는 의도적으로 다른 contexts 들속에 포함된 동일한 객체를 반복적으로 식별하기 위한 특별한 식별자로서 이용될 수도 있다

     

    For example, in Microsoft's Component Object Model,

    every component must implement the IUnknown interface, which is done by creating a UUID representing IUnknown.

     

    In all cases wherever IUnknown is used, whether it is being used by a process trying to access the IUnknown interface in a component, or by a component implementing the IUnknown interface,

    it is always referenced by the same identifier: 00000000-0000-0000-C000-000000000046.

     

    예를 들어 Microsoft#99 의 Component Object Model에서

    모든 컴포넌트는 IUnknown 인터페이스를 구현해야 하는데, 이러한 인터페이스는  IUnknown 을 식별하는 UUID를 생성함으로서 이루어진다.

     

    IUnknown 이 이용되는 모든 경우에, 예를들어서 이 것이 임의의 컴포넌트에서 IUnknown 인터페이스를 처리하려고 시도하는 프로세스에 의해 이용되거나, 또는 IUnknown 인터페이스를 구현하려는 컴포넌트에 의해 이용되는 경우,

     IUnknown 객체 항상 동일한 UUID 식별자 00000000-0000-0000-C000-000000000046 로 참조된다

     

     

    UUID의 한계

     

     

    1. 분산 컴퓨팅 환경에서 따로 따로 구현하는 프로세스 또는 정보를 공유 참조하기 위해 128bit (32 hexadecimal digits)로 이루어지는 컴퓨터를 위한 범세계적으로 고유한 식별자이다

     

    2. UUID 코드는 UTC 타임을 기반으로 시각정보를 자동으로 생성하여 포함하는데, 시간정보가 없는 경우 범세계적으로 고유한 식별아이디 생성이 안될 수 있으므로 수동으로 UUID를 고유하게 생성해야 한다

     

    3. 코드가 16진법 숫자로 이루어고 길어서 컴퓨터의 도움이 없이 사람이 외거나 검색 등 일상적으로 할용하기에는 한계가 있는 식별아이디로 사람중심의 유비쿼터스 서비스에는 부적절하다.

     

     

    해결방안

     

     

    또 하나의 범세계적으로 고유한 식별아이디 ~ePosition  (국제표준 과제: ISO 19151, NWIP: ISO N2225)

     

     

           ePosition ID (예) : (클릭 !)  ~ 자전거묘기의신대니맥아스킬동영상#마이펀

     

             (임의의 Text String ) -> 문자열로 되어 있고 사람 중심의 유비쿼터스 서비스에 적절함. 클릭하면 링크로 콘텐츠에 자동 연결됨






    ePosition 식별아이디는 기존의 표준 DNS 규격을 준수하므로

    별도의 중앙 Registry 가 필요없이 상호 연결된다.

    모든 ePosition ID는 분산된 ePosition 서버에 등록 저장되며

    이는 모든 이메일 주소가 분산된 이메일 서버에 등록 저장되는 것과 유사하다.

    이메일(eMail)에 @ 기호가 사용되는 것과 유사하게(예, ceo@egosio.com)

    이포지션(ePosition)에는 # 기호가 구분기호로 사용된다 ( 예, gg21#egosio.com )

     

    이메일(eMail)은 사람을 식별하는 인터넷 주소로 사람간에 전자문서를 송수신하는 목적으로 만들어진 것이고

    이포지션(ePosition)은 사물(U-센서, 공간객체, 위치정보, 웹기반정보, 가상공간 아바타 등)을 식별하는 인터넷 주소로분산된 서버에 저장된 다양한 정보들을 식별하고 참조하는 목적으로 만들어진 것이다

     





    기존의 RFID 태그에 기록된 코드는 이진수(binary) 코드로 이루어지고 규격화된 표준 형식을 따르기 때문에

    기계적으로 읽고 해석하기 위한 RFID 리더기(RFID reader)가 없이는 정보서버에 등록 저장된 정보를 읽을 수가 없다

     

    기존의 RFID  태그에 기록된 이진수 코드는 그대로 활용하되

    각각의 RFID 태그에 ePosition ID를 추가로 정하여 RFID 응용서비스 서버에 등록 저장하면

    RFID 리더기 없이 ePosition ID 만으로도 해당 RFID 응용 정보서버에 접속하여 원하는 정보를 참조할 수 있다.

     

    대표적인 서비스로 스마트폰 앱(App)이나 WEB(www.eposition.com)에서

    이포지션(ePosition, 예로 NIH#99 )을 문자열로 입력하고 검색하면

    마치 RFID 리더기로 RFID 태그를 읽은 것과 동일한 효과를 얻게 된다

     

    기계의 도움을 받지 않고도 사람중심의 유비쿼터스 서비스가 구현되는 것이다.

    만일 기존의 방식대로 RFID 리더기로 태그를 읽고 싶으면 그대로 하면 되고......(일석이조)

     

     


     
    크리에이티브 커먼즈 라이센스
    Creative Commons License
    2011/07/20 16:58 2011/07/20 16:58
    받은 트랙백이 없고, 댓글이 없습니다.

    댓글+트랙백 RSS :: http://blog.hwport.com/rss/response/628

    댓글+트랙백 ATOM :: http://blog.hwport.com/atom/response/628


    본 내용은 "http://www.joinc.co.kr/modules/moniwiki/wiki.php/Site/Web/documents/UsedCurl?action=print" 에서 발췌한 것임을 밝힙니다.



     Site/Web/documents/UsedCurl
    원문 : http://www.linuxdevcenter.com/pub/a/linux/2005/05/05/libcurl.html?page=1

    Contents

    1 Curl에 대해서
    2 Curl 사용 기초
    3 HTTP GET:웹 페이지 긁어오기
    4 FTP Download: FTP 데이터 다운로드
    5 FTP 업로드
    6 HTTP POST


    1 Curl에 대해서

    curl은 데이터 전송과 관련된 프로그램의 빠른 작성을 위해서 사용하는 command line tool이다. HTTP, FTP, LDAP, TELNET, HTTPS, DICT와 같은 프로토콜을 지원하며, SSL을 가지는 각각의 프로토콜 역시 지원한다. 또한 HTTP기반의 upload, proxies, cookies, user+password 인증을 사용할 수도 있다.

    이러한 툴의 제공과는 별도로 위의 프로토콜들을 지원하는 클라이언트의 제작을 도와주기 위한 libCURL을 제공한다. libCURL을 이용하면 데이터 전송과 관련된 프로그램을 빠르게 작성할 수 있다.

    이 문서는 libCURLAPI를 이용해서 필요한 데이터전송 프로그램을 만드는 법에 대해서 설명을 할 것이다. 쉬운 설명을 위해서 다음과 같은 예제를 작성하게 될 것이다.
    • HTTP GET : URL로 부터 내용을 가져온다.
    • Anonymous FTP 다운로드 : FTP서버에 접속해서 파일을 가져온다.
    • HTTP POST: Web Form을 흉내내어서 데이터를 전송한다.
    • Authenticated FTP Upload : ID+Password로 접속해서 파일을 Upload한다.
    libCURL은 C 라이브러리이다. 몇몇 예제는 빠른작성을 위해서 C++을 이용할것이긴 하지만, 이해하고 사용하는데 무리는 없을 것이다 - C++의 템플릿은 프로그램 작성에 걸리는 시간을 대폭 줄요준다. 테스트 프로그램을 만들고자 할때 시간을 절약하도록 해 준다 -.

    예제는 Ubuntu 환경에서 테스트되었다. libCURL의 버젼은 7.12.3을 사용할 것이다. 버젼이 달라도 큰 문제는 없을 것이라 생각된다.

    2 Curl 사용 기초

    Curl을 사용하기 전에 client/server 에서의 요청과 응답과정이 어떻게 이루어지는지 알아보도록 하자. HTTP 프로토콜을 이용해서 설명하도록 하겠다.
    1. clinet는 server로 연결을 시도한다.
    2. 연결이 되었다면 client는 GET, POST를 이용해서 데이터를 보낼 것이다.
    3. server는 요청을 받고 요청에 대한 응답을 보낸다.(HTML페이지 혹은 에러 메시지)
    4. client는 server로의 연결을 종료한다.
      <!>TCP/IP문서를 참고하기 바란다.
    libCURL은 이러한 프로세스의 중간에서 중계역할을 한다. 사용은 context 객체와 요청데이터 (URL, 인자)와 콜백(callback functions)형식의 response handler를 정의해 주면된다. 이 객채를 라이브러리에 넘기게 되면, 라이브러리는 저수준에서 일어나는 네트워크 통신을 알아서 핸들하고 그 결과 값을 response handler를 통해서 넘겨주게 된다.

    libCURL은 고수준의 라이브러리로 프로그래머는 프로토콜이 어떻게 생겨먹었는지에 대해서 신경쓸 필요 없이, 데이터만 넘겨주는 정도로 필요한 프로그램을 작성할수 있다. libCURL이 Application 계층아래를 완전히 추상화 시켜주기 때문이다.

    libCURL은 아래와 같은 easy라는 이름을 가지는 인터페이스를 제공한다.
    1. curl_blogal_init : curl 라이브러리를 초기화 한다.
    2. curl_easy_init : context를 생성한다.
    3. curl_easy_setopt : context 설정
    4. curl_easy_perform : 요청을 초기화 하고 callback함수를 대기시킨다.
    5. curl_easy_cleanup : context를 없앤다.
    6. curl_global_cleanup : curl 라이브러리를 없앤다.
    curl_easy_setopt 함수는 다음과 같이 선언되어 있다.
    curl_easy_setopt(CURL *ctx, CURLoption key, value) 
     

    3 HTTP GET:웹 페이지 긁어오기

    이제 프로그램을 만들어보도록 하자. 이 프로그램은 HTTP GET을 이용해서 웹페이지를 긁어오는 일을 한다. 헤더는 표준에러로 body 부분은 표준출력형식으로 가져오도록 하겠다.

    curl_easy_init 함수를 호출해서 context 객체를 생성한다.
    CURL *ctx = curl_easy_init (); 
     
    curl_easy_setopt 를 이용해서 context객체를 설정한다. CURLOPT_URL은 목표 URL이다.
    curl_easy_setopt(ctx, CURLOPT_URL, argv[1]); 
     
    curl_easy_setopt를 이용하면 이외에도 몇 가지 설정을 더 해줄 수 있다.
    curl_easy_setiopt(ctx, CURLOPT_WRITEHEADER, stderr); 
    curl_easy_setiopt(ctx, CURLOPT_WRITEDATA, stdout); 
     
    header 정보는 표준에러로, body정보는 표준출력으로 가져오도록 설정을 했다.

    이제 curl_easy_perform함수를 이용해서 실제 페이지를 긁어오는 일을 하도록 하자.
    const CURLcode rc = curl_easy_perform(ctx); 
    if (CURLE_OK != rc) 
    { 
        std::cerr << "Error from cURL: " << curl_easy_strerror(rc) std::endl; 
    } 
    else 
    { 
        // 데이터 처리 
    } 
     

    이후 데이터는 아래와 같이 curl_easy_getinfo를 통해서 얻어와서 처리하면 된다.
    long statLong; 
    curl_easy_getinfo(ctx, CURLINFO_HTTP_CODE, &statLong); 
    std::cout << "HTTP response code: " << statLong << std::endl; 
     
    원하는 값을 가져오기 위해서는 데이터의 타입에 맞는 인자를 써야 한다. 200이나 404와 같은 HTTP 응답코드를 가져오길 원한다면 CURLINFO_HTTP_CODE를 전송받은 문서의 크기를 알아내길 원한다면 CURLINFO_SIZE_DOWNLOAD를 사용하는 식이다.

    모든 작업이 다 끝났다면, curl_easy_cleanup을 호출해서 curl_easy_setopt 객체를 소멸시켜야 한다. 그렇지 않을경우 메모리누수 현상을 겪게 될 것이다.
    /* 
    sample for O'ReillyNet article on libcURL: 
        {TITLE} 
        {URL} 
        AUTHOR: Ethan McCallum 
     
    Scenario: use http/GET to fetch a webpage 
     
    이 코드는 Ubuntu 리눅스 Kernel 2.6.15에서  
    libcURL 버젼 7.15.1로 테스트 되었다. 
    2006년 8월 3일 
    */ 
     
    #include<iostream> 
     
    extern "C" { 
        #include<curl/curl.h> 
    } 
     
    // - - - - - - - - - - - - - - - - - - - - 
     
    enum { 
        ERROR_ARGS = 1 , 
        ERROR_CURL_INIT = 2 
    } ; 
     
    enum { 
        OPTION_FALSE = 0 , 
        OPTION_TRUE = 1 
    } ; 
     
    enum { 
        FLAG_DEFAULT = 0  
    } ; 
     
    // - - - - - - - - - - - - - - - - - - - - 
     
    int main( const int argc , const char** argv ){ 
     
        if( argc != 2 ){ 
            std::cerr << " Usage: ./" << argv[0] << " {url} [debug]" << std::endl ; 
            return( ERROR_ARGS ) ; 
        } 
     
        const char* url = argv[1] ; 
     
        // lubcURL 초기화  
        curl_global_init( CURL_GLOBAL_ALL ) ; 
     
        // context객체의 생성 
        CURL* ctx = curl_easy_init() ; 
     
        if( NULL == ctx ){ 
            std::cerr << "Unable to initialize cURL interface" << std::endl ; 
            return( ERROR_CURL_INIT ) ; 
        } 
     
        // context 객체를 설정한다.     
        // 긁어올 url을 명시하고, url이 URL정보임을 알려준다. 
        curl_easy_setopt( ctx , CURLOPT_URL,  url ) ; 
     
        // no progress bar: 
        curl_easy_setopt( ctx , CURLOPT_NOPROGRESS , OPTION_TRUE ) ; 
     
        /* 
        By default, headers are stripped from the output. 
        They can be: 
     
        - passed through a separate FILE* (CURLOPT_WRITEHEADER) 
     
        - included in the body's output (CURLOPT_HEADER -> nonzero value) 
            (here, the headers will be passed to whatever function 
             processes the body, along w/ the body) 
     
        - handled with separate callbacks (CURLOPT_HEADERFUNCTION) 
            (in this case, set CURLOPT_WRITEHEADER to a 
             matching struct for the function) 
     
        */ 
         
        // 헤더는 표준에러로 출력하도록 하다.  
        curl_easy_setopt( ctx , CURLOPT_WRITEHEADER , stderr ) ; 
     
     
        // body 데이터는 표준출력 하도록 한다. 
        curl_easy_setopt( ctx , CURLOPT_WRITEDATA , stdout ) ; 
     
        // context 객체의 설정 종료  
     
     
        // 웹페이지를 긁어온다.  
     
        const CURLcode rc = curl_easy_perform( ctx ) ; 
     
        if( CURLE_OK != rc ){ 
     
            std::cerr << "Error from cURL: " << curl_easy_strerror( rc ) << std::endl ; 
     
        }else{ 
     
            // get some info about the xfer: 
            double statDouble ; 
            long statLong ; 
            char* statString = NULL ; 
     
            // HTTP 응답코드를 얻어온다.  
            if( CURLE_OK == curl_easy_getinfo( ctx , CURLINFO_HTTP_CODE , &statLong ) ){ 
                std::cout << "Response code:  " << statLong << std::endl ; 
            } 
     
            // Content-Type 를 얻어온다. 
            if( CURLE_OK == curl_easy_getinfo( ctx , CURLINFO_CONTENT_TYPE , &statString ) ){ 
                std::cout << "Content type:   " << statString << std::endl ; 
            } 
     
            // 다운로드한 문서의 크기를 얻어온다. 
            if( CURLE_OK == curl_easy_getinfo( ctx , CURLINFO_SIZE_DOWNLOAD , &statDouble ) ){ 
                std::cout << "Download size:  " << statDouble << "bytes" << std::endl ; 
            } 
     
            //  
            if( CURLE_OK == curl_easy_getinfo( ctx , CURLINFO_SPEED_DOWNLOAD , &statDouble ) ){ 
                std::cout << "Download speed: " << statDouble << "bytes/sec" << std::endl ; 
            } 
     
        } 
     
        // cleanup 
        curl_easy_cleanup( ctx ) ; 
        curl_global_cleanup() ; 
     
        return( 0 ) ; 
     
    } // main() 
     

    4 FTP Download: FTP 데이터 다운로드

    현재는 웹서비스를 통해서 데이터를 얻는 경우가 압도적이지만, 여전히 FTP는 데이터의 전송을 위한 용도로 널리 사용되고 있다.

    FTP는 script의 작성을 통해서 일괄작업 형식으로 파일을 업/다운로드 하는 형태로 사용될 수 있다. 이러한 일을 하는 스크립트를 작성하기 위해서는 expect와 같은 툴을 이용해야 하는데, 제대로 작동하는 프로그램을 짜기란 여간 어려운게 아니다. 서버의 특성에 따라서 프로그래밍 기법이 달라져야 하며, 특히 에러처리가 매우 힘들기 때문이다. (ncftp와 같은 프로그램은 스크립트 형태로 사용가능한 ncftpput, ncftpget'과 같은 툴을 제공한다.)

    httpget.cc와 중복되는 내용들은 빼고 설명하도록 하겠다. 이 프로그램은 원격지의 파일을 로컬에 저장하지는 않는다. 파일을 읽어들이고, 그 크기만 출력을 한다.
    size_t showSize(...); 
    curl_esay_setopt(ctx, CURLOPT_WRITEFUNCTION, showSize); 
     
    CURLOPT_WRITEFUNCTION은 원격지 파일을 다운로드 할께 호출할 함수를 정의하기 위해서 사용한다.

    이제 다운로드할 파일갯수만큼 for루프를 돌면서 파일을 다운받는다. 우선 CURLOPT_URL을 이용해서 연결을 시도할 서버이름을 정해준다. CURLOPT_WRIDATE 는 CURLOPT_WRITEFUNCTION를 이용해서 정의된 콜백함수 - showSize() -를 할당해서 인자로 주어진 XferInfo객체에 데이터를 쓴다.
    class XferInfo 
    { 
        void add(int more); 
        int getBytesTransferred(); 
        int getTimesCalled(); 
    }; 
    ... 
     
    XferInfo info; 
    curl_easy_setopt(ctx, CURLOPT_WRITEDATA, &info); 
     

    그럼 실질적으로 데이터를 받아서 처리하는 showSize 콜백함수에 대해서 알아보도록 하자.
    extern "C" 
    size_t showSize 
    { 
        void *source, 
        size_t size, 
        size_t nmemb, 
        void *userData 
    } 
     

    source는 읽어들인 실제 데이터다. 읽어들인 데이터는 NULL을 포함하는 이진데이터가 될 수 있으므로, char *형 대신 void * 형을 사용했다. 읽어들일 데이터의 크기는 size*nmemb를 통해 계산한다.

    userData는 CURLOPT_WRITEDATA 에 의해서 할당된, 자료구조로 우리는 여기에 필요한 정보 - 읽어들인 파일의 크기 - 를 적을 것이다. userData자료구조 대신에 파일에 쓰길 원한다면 FILE *을 넘기면 된다. usrDatavoid *형이므로 반드시 캐스트 해주어야 한다.
    extern "C" 
    size_t showSize(...) 
    { 
        XferInfo *info = static_cast<XferInfo *>(userData); 
        const int bufferSize = size * nmemb; 
        info->add(bufferSize); 
    } 
     
    이제 우리가 만든 콜백함수는 읽어들인 파일의 크기를 (size*nmemb)로 계산해서 되돌려줄 것이다. 또한 이 함수는 bufferSize를 되돌려주게 되는데, libCURL은 콜백함수의 리턴값과 전송받은 실제 데이터의 크기를 비교해서 틀릴경우 0을 리턴해주게 된다. 이런식으로 데이터를 정상적으로 받았는지를 체크할 수 있다.

    이제 curl_easy_perform을 이용해서 URL에 연결해서 데이터를 받아오고 콜백함수를 실행시키면 된다.
    /* 
     
    sample for O'ReillyNet article on libcURL: 
        {TITLE} 
        {URL} 
        AUTHOR: Ethan McCallum 
     
     
    anon ftp/download (scenario: fetch remote file) 
     
    이 코드는 Ubuntu 리눅스 Kernel 2.6.15에서 
    libcURL 버젼 7.15.1로 테스트 되었다. 
    2006년 8월 3일 
     
    */ 
     
    #include<iostream> 
    #include<string> 
    #include<sstream> 
    #include<list> 
     
    extern "C" { 
        #include<curl/curl.h> 
    } 
     
    // - - - - - - - - - - - - - - - - - - - - 
     
    typedef std::list< std::string > FileList ; 
     
    enum { 
        ERROR_ARGS = 1 , 
        ERROR_CURL_INIT = 2 
    } ; 
     
    enum { 
        OPTION_FALSE = 0 , 
        OPTION_TRUE = 1 
    } ; 
     
    // - - - - - - - - - - - - - - - - - - - - 
     
    // 콜백함수에서 사용할 사용자 정의 객체  
    class XferInfo { 
     
        private: 
        int bytesTransferred_ ; 
        int invocations_ ; 
     
        protected: 
        // empty 
     
        public: 
        XferInfo(){ 
     
            reset() ; 
     
        } // ctor 
     
        /// reset counters 
        void reset(){ 
     
            bytesTransferred_ = 0 ; 
            invocations_ = 0 ; 
     
            return ; 
     
        } // reset() 
     
        /// add the number of bytes transferred in this call 
        void add( int more ){ 
     
            bytesTransferred_ += more ; 
            ++invocations_ ; 
     
            return ; 
     
        } // add()     
     
        /// get the amount of data transferred, in bytes 
        int getBytesTransferred() const { 
            return( bytesTransferred_ ) ; 
        } // getBytesTransferred() 
     
        /// get the number of times add() has been called 
        int getTimesCalled(){ 
            return( invocations_ ) ; 
        } // getTimesCalled() 
    } ; 
     
    // - - - - - - - - - - - - - - - - - - - - 
     
     
    // C++ 에서 C함수를 링크시키기 위해서는, "extern C"를 이용해야 한다. 
    extern "C" 
    size_t showSize( void *source , size_t size , size_t nmemb , void *userData ){ 
     
        // this function may be called any number of times for even a single 
        // transfer; be sure to write it accordingly. 
     
        // source is the actual data fetched by libcURL; cast it to whatever 
        // type you need (usually char*).  It has NO terminating NULL byte. 
     
        // we don't touch the data here, so the cast is commented out 
        // const char* data = static_cast< const char* >( source ) ; 
     
        // userData is called "stream" in the docs, which is misleading: 
        // that parameter can be _any_ data type, not necessarily a FILE* 
        // Here, we use it to save state between calls to this function 
        // and track number of times this callback is invoked. 
        XferInfo* info = static_cast< XferInfo* >( userData ) ; 
     
        const int bufferSize = size * nmemb ; 
     
        std::cout << '\t' << "showSize() called: " << bufferSize << " bytes passed" << std::endl ; 
     
        // ... pretend real data processing on *source happens here ... 
     
        info->add( bufferSize ) ; 
     
             
        /* 
        return some number less than bufferSize to indicate an 
        error (xfer abort) 
         
        nicer code would also set a status var (in userData) for the 
        calling function 
        */ 
     
        return( bufferSize ) ; 
     
    } // showSize() 
     
     
    // - - - - - - - - - - - - - - - - - - - - 
     
     
    int main( const int argc , const char** argv ){ 
     
        if( argc < 3 ){ 
            std::cerr << "test of libcURL: anonymous FTP" << std::endl ; 
            std::cerr << " Usage: " << argv[0] << " {server} {file1} [{file2} ...]" << std::endl ; 
            return( ERROR_ARGS ) ; 
        } 
     
        // remote FTP server 
        const char* server = argv[1] ; 
     
        const int totalTargets = argc - 2 ; 
     
        std::cout << "Attempting to download " << totalTargets 
            << " files from " << server << std::endl 
        ; 
     
     
        curl_global_init( CURL_GLOBAL_ALL ) ; 
     
     
        CURL* ctx = curl_easy_init() ; 
     
        if( NULL == ctx ){ 
            std::cerr << "Unable to initialize cURL interface" << std::endl ; 
            return( ERROR_CURL_INIT ) ; 
        } 
     
        /* BEGIN: global handle options */ 
     
        // handy for debugging: see *everything* that goes on 
        // curl_easy_setopt( ctx , CURLOPT_VERBOSE, OPTION_TRUE ) ; 
     
        // no progress bar: 
        curl_easy_setopt( ctx , CURLOPT_NOPROGRESS , OPTION_TRUE ) ; 
     
        // what to do with returned data 
        curl_easy_setopt( ctx , CURLOPT_WRITEFUNCTION , showSize ) ; 
     
        XferInfo info ; 
     
        for( 
            int ix = 2 ; 
            ix < argc ; 
            ++ix 
        ){ 
     
            const char* item = argv[ ix ] ; 
     
            // zero counters for each file 
            info.reset() ; 
     
     
            // target url: concatenate the server and target file name 
            urlBuffer.str( "" ) ; 
            urlBuffer << "ftp://" << server << "/" << item << std::flush ; 
     
            std::cout << "Trying " << urlBuffer.str() << std::endl ; 
     
            const std::string url = urlBuffer.str() ; 
     
            curl_easy_setopt( ctx , CURLOPT_URL,  url.c_str() ) ; 
     
            // set the write function's user-data (state data) 
            curl_easy_setopt( ctx , CURLOPT_WRITEDATA , &info ) ; 
     
     
            // action! 
     
            const CURLcode rc = curl_easy_perform( ctx ) ; 
     
            // for curl v7.11.x and earlier, look into 
            // curl_easy_setopt( ctx , CURLOPT_ERRORBUFFER , /* char array */ ) ; 
            if( CURLE_OK == rc ){ 
                std::cout << '\t' << "xfer size: " << info.getBytesTransferred() << " bytes" << std::endl ; 
                std::cout << '\t' << "Callback was invoked " << info.getTimesCalled() << " times for this file" << std::endl ; 
            } else { 
                std::cerr << "Error from cURL: " << curl_easy_strerror( rc ) << std::endl ; 
            } 
     
            std::cout << std::endl ; 
     
        } 
     
     
        // cleanup 
        curl_easy_cleanup( ctx ) ; 
        curl_global_cleanup() ; 
     
        return( 0 ) ; 
     
    } // main() 
     
     

    5 FTP 업로드

    이번에 만들 프로그램은 FTP 호스트에 연결해서 파일을 Upload 하는 일을 한다. FTP호스트에 접근하기 위해서는 host의 도메인 이름과 로그인을 위한 아이디와 패스워드가 필요하다. 제대로된 프로그램이라면 설정파일등을 통해서 이들 정보를 입력받아야 겠지만, 여기에서는 편의상 소스내에 하드코딩하도록 하겠다. 타겟 URL에 file을 업로드하는 것은 다음과 같이 기술된다.
    ftp://host/file 
     

    로그인을 위한 아이디와 패스워드는 CURLOPT_USERPWD를 이용해서 설정할 수 있다. 이들 정보는 다음과 같이 기술될 것이다.
    login:password 
     

    최종적으로 다음과 같은 URL정보를 가지게 된다.
    ftp://login:password@host/file 
     

    libCURL은 파일을 전송하는 것 뿐만 아니라. ftp서버에서 사용할 수 있는 mkdir과 같은 명령도 문제없이 사용할 수 있다. 사용할 명령은 curl_slist 에 링크드리스트 형태로 저장한다.
    struct curl_slist *command=NULL; 
    command = curl_slist_append( commands, "mkdir /some/path"); 
    command = curl_slist_append( commands, "mkdir /another/path"); 
     
    물론 이들 명령이 실행하기 위해서는 로그인 과정을 우선 거쳐야 할 것이다. 이제 CURLOPT_QUOTE를 이용해서 명령을 등록시키면 된다.
    curl_easy_setopt(ctx, CURLPOST_QUOTE, commands); 
    // curl_easy_perform을 이용해서 ftp 세션을 시작하고 
    // 명령을 실행한다. 
    curl_slist_free_all(command); 
     


    6 HTTP POST

    HTTP POST는 웹을 통해서 폼데이터를 주고받기 위한 데이터 전송방법의 하나다. 이 문서의 마지막 예제로 libCURL을 이용해서 HTTP POST 형식의 데이터를 전송하는 방법에 대해서 알아보도록 하겠다. 또한 사용자정의된 HTTP header도 테스트하도록 하겠다. 완전한 테스트를 위해서는 웹서버가 구축되어 있어야 할 것이다.

    POST로 보내는 데이터는 key=value형태로 되어 있으며, 각각의 key=value 는 &를 통해서 구분된다.
    const char* postData="param1=value1¶m2=value2&..."; 
     
    POST 데이터 전송을 위해서 CURLOPT_POSTFIELDS옵션을 설정하면 된다.
    curl_easy_setopt(ctx, CURLOPT_POSTFIELDS, postData); 
     

    이제 CURLOPT_HTTPHEADER를 이용해서 사용자정의 HTTP 헤더를 만들도록 한다.
    curl_sist * responseHeaders=NULL; 
     
    responseHeaders = curl_slist_append( 
        responseHeaders, 
        "Expect: 100-continue" 
    ); 
     
    curl_easy_setopt(ctx, CURLOPT_HTTPHEADER, responseHeaders); 
     
    주의 할것은 libCURL은 hidden 필드나 JavaScript와 같은 클라이언트측의 기술들을 사용하지 못한다는 점이다. 예를들어 폼입력을 하고나서 submit버튼을 클릭하면 폼의 각 필드를 검사하는 등의 자바스크립트등은 처리할 수 없다.

    /* 
     
    sample for O'ReillyNet article on libcURL: 
        {TITLE} 
        {URL} 
        AUTHOR: Ethan McCallum 
     
    HTTP POST (e.g. form processing or REST web services) 
     
    이 코드는 Ubuntu 6.06 Dapper Drake,   
    libcURL 
    This code was built/tested under Fedora Core 3, 
    libcURL version 7.12.3 환경에서 테스트 되었다. 
    */ 
     
    #include<cstdio> 
    #include<iostream> 
    #include<string> 
    #include<sstream> 
     
    extern "C" { 
        #include<curl/curl.h> 
    } 
     
    // - - - - - - - - - - - - - - - - - - - - 
     
    enum { 
        ERROR_ARGS = 1 , 
        ERROR_CURL_INIT = 2 
    } ; 
     
    enum { 
        OPTION_FALSE = 0 , 
        OPTION_TRUE = 1 
    } ; 
     
    enum { 
        FLAG_DEFAULT = 0  
    } ; 
     
    const char* targetUrl ; 
     
    // - - - - - - - - - - - - - - - - - - - - 
     
    int main( int argc , char** argv ){ 
     
        if( argc != 2 ){ 
            std::cerr << "test of libcURL: test an HTTP post" << std::endl ; 
            std::cerr << "(post data is canned)" << std::endl ; 
            std::cerr << " Usage: " << argv[0] << " {post url}" << std::endl ; 
            std::exit( ERROR_ARGS ) ; 
        } 
     
        targetUrl = argv[1] ; 
     
        curl_global_init( CURL_GLOBAL_ALL ) ; 
     
     
        CURL* ctx = curl_easy_init() ; 
     
        if( NULL == ctx ){ 
            std::cerr << "Unable to initialize cURL interface" << std::endl ; 
            return( ERROR_CURL_INIT ) ; 
        } 
     
        /* BEGIN: configure the handle: */ 
     
        // Target URL:  
        curl_easy_setopt( ctx , CURLOPT_URL,  targetUrl ) ; 
        // no progress bar: 
        curl_easy_setopt( ctx , CURLOPT_NOPROGRESS , OPTION_TRUE ) ; 
     
        // 응답데이터를 표준출력으로 보낸다.  
        curl_easy_setopt( ctx , CURLOPT_WRITEDATA , stdout ) ; 
     
        // 사용자 정의 HTTP 헤더: create a linked list and assign 
        curl_slist* responseHeaders = NULL ; 
        responseHeaders = curl_slist_append( responseHeaders , "Expect: 100-continue" ) ; 
        responseHeaders = curl_slist_append( responseHeaders , "User-Agent: Some Custom App" ) ; 
        curl_easy_setopt( ctx , CURLOPT_HTTPHEADER , responseHeaders ) ; 
     
        // POST Data 설정  
        // notice the URL-unfriendly characters (such as "%" and "&") 
        // URL에서는 '%', '&', ' '와 같은 문자를 URL encoding 시켜줘야 한다. 
        // curl_escape 함수를 이용해서 인코딩할 수 있다.  
        const char* postParams[] = { 
            "One"      , "this has % and & symbols" , 
            "Dos"      , "value with spaces" , 
            "Trois"    , "plus+signs+will+be+escaped" , 
            "Chetirye" , "fourth param..." , 
            NULL 
        } ;  
     
        // buffer for the POST params 
        std::ostringstream postBuf ; 
     
        const char** postParamsPtr = postParams ; 
     
        while( NULL != *postParamsPtr ) 
        { 
            // curl_escape( {string} , 0 ): replace special characters 
            // (such as space, "&", "+", "%") with HTML entities. 
            // ( 0 => "use strlen to find string length" ) 
            // remember to call curl_free() on the strings on the way out 
            char* key = curl_escape( postParamsPtr[0] , FLAG_DEFAULT ) ; 
            char* val = curl_escape( postParamsPtr[1] , FLAG_DEFAULT )  ; 
     
            std::cout << "Setting POST param: \"" << key << "\" => \"" << val << "\"" << std::endl ; 
            postBuf << key << "=" << val << "&" ; 
     
            postParamsPtr += 2 ; 
     
            // the cURL lib allocated the escaped versions of the 
            // param strings; we must free them here 
            curl_free( key ) ; 
            curl_free( val ) ; 
     
        } 
        postBuf << std::flush ; 
     
        // We can't really call "postBuf.str().c_str()" here, because 
        // the std::string created in the middle is a temporary.  In turn, 
        // the char* buf from its c_str() operation isn't guaranteed to 
        // be around after the function call. 
        // The solution: explicitly create the string. 
     
        // Larger (and/or better) code would use std::string::copy() to create 
        // a const char* pointer to pass to cURL, then clean it up later. 
        // e.g.: 
        //    const char* postData = new char*[ 1 + postBuf.tellg() ] ; 
        //     postBuf.str().copy( postData , std::string::npos ) ; 
        //  postData[ postBuf.tellg() ] == '\0' ; 
        const std::string postData = postBuf.str() ; 
     
        std::cout << "post data: " << postData << std::endl ; 
        curl_easy_setopt( ctx , CURLOPT_POSTFIELDS , postData.c_str() ) ; 
     
        // do a standard HTTP POST op 
        // in theory, this is automatically set for us by setting 
        // CURLOPT_POSTFIELDS... 
        curl_easy_setopt( ctx , CURLOPT_POST , OPTION_TRUE ) ; 
     
        /* END: configure the handle */ 
        // action! 
        std::cout << "- - - BEGIN: response - - -" << std::endl ; 
        CURLcode rc = curl_easy_perform( ctx ) ; 
        std::cout << "- - - END: response - - -" << std::endl ; 
     
        // "curl_easy_strerror()" available in curl v7.12.x and later 
        if( CURLE_OK != rc ){ 
            std::cerr << '\t' << "Error from cURL: " << curl_easy_strerror( rc ) << std::endl ; 
        } 
     
        // cleanup 
        curl_slist_free_all( responseHeaders ) ; 
        curl_easy_cleanup( ctx ) ; 
        curl_global_cleanup() ; 
        std::exit( 0 ) ; 
    } // main() 
     

    위 프로그램의 테스트를 위해서 다음과 같은 php페이지를 만들었다.
    <? 
        echo "SERVER Ons      : $One<br>\n"; 
        echo "SERVER Dos      : $Dos<br>\n"; 
        echo "SERVER Trois    : $Trois<br>\n"; 
        echo "SERVER Chetirye : $Chetirye<br>\n"; 
    ?> 
     
    Error cache
    크리에이티브 커먼즈 라이센스
    Creative Commons License
    2011/06/22 14:45 2011/06/22 14:45
    TAG
    받은 트랙백이 없고, 댓글이 없습니다.

    댓글+트랙백 RSS :: http://blog.hwport.com/rss/response/626

    댓글+트랙백 ATOM :: http://blog.hwport.com/atom/response/626

    본 내용은 http://wariua.springnote.com/pages/7143229 에서 발췌한 것임을 밝힙니다.


    fanotify API

    By Jonathan Corbet, 2009년 7월 1일

    2.6.31에 포함된 사항들 중에 "fsnotify" 파일 이벤트 알림 프레임워크가 있습니다. fsnotify는 inotify와 dnotify API의 새로운 공통의 토대 역할을 하며, 코드를 상당히 단순하게 만들어 줍니다. 단순화는 분명 환영할 만한 것이지만 fsnotify의 진짜 목적은 아니었습니다. fsnotify가 존재하는 건 그보다는 리뷰를 위해 따로 포스팅 한 fanotify, 즉 "fscking all notification system"의 지지 구조 역할을 하기 위해서입니다.

    fanotify는 이전에 TALPA라고도 했으며, 주된 목적은 리눅스 시스템에서 악성코드 탐색기(malware scanner) 구현을 가능케 하는 것입니다. TALPA에 대한 제안이 처음 이뤄졌을 때 여러 면에서 비판을 받았는데, 그 가운데에는 보안 기법으로서의 악성코드 탐색에 대한 일반적인 업신여김도 적지 않았습니다. 하지만 슬프게도 현실은 많은 고객들이 이 기능을 요구한다는 것이고, 따라서 리눅스에서 그런 제품들에 대한 시장이 존재합니다. 지금까지 리눅스의 탐색형 제품들은 여러 가지 아름답지 않은 기법들을 이용해 왔습니다. 시스템 호출 테이블에 후킹을 하거나 바이너리로만 제공하는 보안 모듈을 적재하는 식이었습니다. 이런 제품들이 그런 사파의 기법들들 버리고 커널에서 멀찌감치 떨어지도록 하는 데 fanotify가 도움이 되리라 생각해 봅니다.

    fanotify가 쓰는 사용자 공간 API는 좀 이상해 보일 수 있습니다. fanotify 응용 프로그램은 먼저 새로운 PF_FANOTIFY 프로토콜족의 소켓을 엽니다. 이 소켓은 다음과 같이 기술할 수 있는 "주소"에 바인드 시켜야 합니다.

    1. struct fanotify_addr {
      sa_family_t family;
      __u32 priority;
      __u32 group_num;
      __u32 mask;
      __u32 timeout;
      __u32 unused[16];
      };

    family 필드는 AF_FANOTIFY여야 합니다. priority 필드는 여러 fanotify 소켓이 존재할 때 어떤 이벤트를 어느 소켓이 가지게 될지 결정하는 데 씁니다. 낮은 우선순위 값이 이깁니다. group_num은 fsnotify 계층에서 이벤트 리스너의 그룹을 식별하는 데 씁니다. timeout 필드는 현재 안 쓰이는 것 같습니다. 마지막으로 mask는 응용 프로그램이 듣고 싶어하는 이벤트를 기술합니다.

    • FAN_ACCESS: 모든 파일 접근.
    • FAN_MODIFY: 파일 변경.
    • FAN_CLOSE: 파일을 닫을 때.
    • FAN_OPEN: open() 호출.
    • FAN_ACCESS_PERM: FAN_ACCESS와 비슷하되, fanotify 클라이언트가 연산을 허용할지 결정하는 동안 파일에 접근하려고 시도하는 프로세스를 잡아둠.
    • FAN_OPEN_PERM: FAN_OPEN과 비슷하되, 권한을 검사함.
    • FAN_EVENT_ON_CHILD: 호출자가 디렉터리 구조 전체의 이벤트에 관심이 있음.
    • FAN_GLOBAL_LISTENER: 시스템의 모든 파일에 대한 이벤트를 알려줌.

    소켓을 바인드 하고 나면 응용 프로그램은 아주 유명한 이벤트 읽기 시스템 호출인 getsockopt()를 이용해 파일 시스템 활동에 대해 알 수 있습니다. 레벨에 SOL_FANOTIFY를 주소 옵션에 FANOTIFY_GET_EVENT를 줘서 getsockopt()를 호출하면 다음과 같은 구조체를 하나 혹은 그 이상 반환해 줍니다.

    1. struct fanotify_event_metadata {
      __u32 event_len;
      __s32 fd;
      __u32 mask;
      __u32 f_flags;
      pid_t pid;
      pid_t tgid;
      __u64 cookie;
      };

    여기서 fd는 문제의 파일에 대해 열린 읽기 전용의 파일 디스크립터이고, mask는 (위에서 설명한 플래그들로) 이벤트를 기술하며, f_flags는 파일에 접근하려고 시도하는 프로세스가 전달한 플래그의 사본이며, pidtgid는 그 프로세스를 식별합니다 (현재는 이름 공간까지는 다루지 못함). 응용 프로그램의 허가를 요구하는 이벤트인 경우 cookie에 허용 및 거절 때 사용할 값이 담겨있습니다.

    그렇게 받은 파일 디스크립터를 언젠간 닫아주어야 한다는 걸 읽지 마십시오. 안 그러면 파일 디스크립터가 상당히 빠르게 쌓여버릴 수도 있습니다.

    접근 허가 여부를 결정할 때 응용 프로그램은 FANOTIFY_ACCESS_RESPONSE 옵션과 다음 구조체로 setsockopt()를 호출해서 커널에게 알려주어야 합니다.

    1. struct fanotify_so_access {
      __u64 cookie;
      __u32 response;
      };

    이벤트에서 얻은 cookie 값을 넣어줘야 하며, responseFAN_ALLOWFAN_DENY 중 하나여야 합니다. 응용 프로그램이 5초 안에 응답하지 않으면 커널은 그 동작이 계속 진행되도록 허용하게 됩니다. 5초라는 값은 파일 탐색형 프로그램에는 충분하겠지만, 계층적 기억장치 관리 시스템 같은 다른 fanotify 응용에서는 문제가 될 수도 있습니다. fanotify 개발자인 Eric Paris는 응답을 무한정 지연시킬 수 있는 옵션을 아마도 언젠가 추가하게 될 거라고 언급하고 있습니다.

    알림 대상 파일의 집합을 FANOTIFY_SET_MARK, FANOTIFY_REMOVE_MARK, FANOTIFY_CLEAR_MARKS 연산으로 조정할 수도 있습니다. 바인드 할 때 FAN_GLOBAL_LISTENER 옵션을 준 경우라면 처음부터 모든 파일이 "표시됨" 상태이며, 여기서 FANOTIFY_REMOVE_MARK를 써서 관심 없는 파일들을 쳐낼 수 있습니다. 그렇지 않은 경우라면 적어도 한 번은 FANOTIFY_SET_MARK 호출을 해줘야 이벤트를 받게 됩니다.

    몇 가지 세세한 내용을 빼먹긴 했지만 위 내용에서 fanotify API의 핵심 부분을 모두 다루고 있습니다. 이쪽 포스팅에 대한 댓글은 꽤 드문 편이었습니다. 그리고 이 기능에 대한 반대는 최근 일이 년 사이에 사라진 것 같습니다. 이제 남은 건 API를 제대로 만드는 것이겠지요. 편집자는 이벤트 가져오기 인터페이스로 getsockopt()를 쓰는 방식에 대해 언젠가 몇몇 사람들이 눈살을 찌푸리지 않을까 걱정합니다. 하지만 이 기능을 깔끔하게 만들었을 때쯤이면 리눅스는 또 다른 범용적인 파일 접근 알림 및 허용 인터페이스를 쓰는 쪽으로 가고 있지 않을까 싶습니다.


    (역자 편집) 관련글에서 가져온 사용 예:

    1. int main(void) {
      int fan_fd, len;
      struct fanotify_addr addr;
      socklen_t socklen;
      char buf[4096];
      struct fanotify_event_metadata *metadata;

      memset(&addr, 0, sizeof(addr));
      addr.family = AF_FANOTIFY;
      addr.group_num = 123456;
      addr.priority = 32768;
      addr.mask = FAN_OPEN | FAN_GLOBAL_LISTENER;

      fan_fd = socket(PF_FANOTIFY, SOCK_RAW, 0);
      bind(fan_fd, (struct sockaddr *)&addr, sizeof(addr));
      while (1) {
      socklen = sizeof(buf);
      getsockopt(fan_fd, SOL_FANOTIFY, FANOTIFY_GET_EVENT,
      buf, &socklen);
      metadata = &buf;
      len = socklen;
      while(FAN_EVENT_OK(metadata, len)) {
      printf("got event!\n");
      close(metadata->fd);
      metadata = FAN_EVENT_NEXT(metadata, len);
      }
      }
      }
    크리에이티브 커먼즈 라이센스
    Creative Commons License
    2011/06/16 01:14 2011/06/16 01:14
    TAG
    받은 트랙백이 없고, 댓글이 없습니다.

    댓글+트랙백 RSS :: http://blog.hwport.com/rss/response/625

    댓글+트랙백 ATOM :: http://blog.hwport.com/atom/response/625


    본 내용은 http://www.ibm.com/developerworks/kr/library/l-inotify/index.html 에서 발췌한 것임을 밝힙니다.




    Inotify를 이용한 Linux 파일 시스템 모니터

    Linux 2.6 커널에서 효과적인 파일 시스템 이벤트 모니터하기

    Ian Shields, 선임 프로그래머, IBM Japan

    요약: Linux® 파일 시스템 이벤트를 효과적으로 세밀하게 비동기적으로 모니터해야 하는 경우에는 Inotify를 사용할 수 있습니다. Inotify를 사용하면 보안, 성능이나 기타 다른 목적으로 사용자 공간을 모니터할 수 있습니다.

    이 기사에 테그: 보안, 성능

    원문 게재일: 2010 년 4 월 06 일번역 게재일: 2010 년 5 월 04 일
    난이도: 초급영어로: 보기PDF: A4 and Letter (62KB | 18 pages)Get Adobe® Reader®
    페이지뷰: 3324 회
    의견: 0 (의견 추가)

    1 star2 stars3 stars4 stars5 stars평균 평가 등급 (총 12표)

    Ian과 연락하기

    Ian은 가장 인기있고 많은 글을 쓰는 저자 중 한 명이다. developerWorks에 실린Ian의 모든 기사를 살펴보자. My developerWorks에서 Ian의 프로파일을 볼 수 있으며 Ian과 다른 저자 및 그를 좋아하는 여러 독자와 연락할 수 있다.

    Inotify가 Linux 커널에 최종적으로 통합되기 전에 이 기사의 초기 버전을 작성한 IBM의 Eli Dow에게 감사드린다. 특히, 다운로드 섹션에 있는 샘플 코드는 여전히 Eli의 원본 샘플 코드를 기반으로 작성되었다.

    Inotify 소개

    파일 시스템 이벤트 모니터링은 파일 관리자에서 보안 도구에 이르는 다양한 유형의 프로그램에 필수적인 기능이다. Linux에는 Linux 2.6.13 커널부터Inotify가 포함되었으며 이 도구를 이용하면 모니터링 프로그램에서 하나의 파일 디스크립터를 열어서 하나 이상의 파일이나 디렉토리에서 열기, 닫기, 이동/이름 변경, 삭제, 작성이나 속성 변경과 같은 지정된 이벤트가 발생하는지 감시할 수 있다. 이후 커널에서는 몇 가지 기능이 개선되었으므로 이러한 기능을 사용하기 전에 커널 레벨을 확인한다.

    이 기사에서는 간단한 모니터링 애플리케이션에서 Inotify 기능을 사용하는 방법을 살펴본다. 샘플 코드를 다운로드한 다음, 진행을 위해 해당 시스템에서 컴파일한다.


    간단한 히스토리

    Inotify가 있기 전에는 dnotify가 있었다. 불행히도 dnotify에는 제한사항이 있었으며 이 때문에 사용자들은 좀 더 기능이 개선되기를 원했다. Inotify의 장점은 다음과 같다.

    • Inotify는 하나의 파일 디스크립터를 사용하는 데 비해 dnotify에서는 변화를 감시할 각 디렉토리마다 하나의 파일 디스크립터를 열어야 한다. 이렇게 되면 한 번에 여러 개의 디렉토리를 모니터할 경우, 자원의 손실이 커지게 되며 프로세스당 파일 디스크립터 제한에 걸릴 수도 있다.
    • Inotify에서 사용하는 파일 디스크립터는 시스템 호출을 사용하여 얻게 되며 연관된 장치나 파일이 없다. dnotify를 사용하면 파일 디스크립터가 디렉토리에 고정되고 지원 장치가 마운트 해제되지 않게 되어 이동식 매체에 특수한 문제를 일으킨다. Inotify를 사용하는 경우에는 마운트 해제된 파일 시스템에 있는 감시 파일이나 디렉토리에서 이벤트가 생성되면 감시가 자동으로 제거된다.
    • Inotify는 파일이나 디렉토리를 감시할 수 있다. Dnotify는 디렉토리를 모니터하며 따라서 프로그래머는 감시할 디렉토리에 있는 파일이 반영된 동등한 데이터 구조나 상태 구조를 유지한 다음, 이벤트가 발생한 이후에 현재의 상태와 비교하여 디렉토리에 있는 항목에서 발생한 변화를 확인해야 했다.
    • 위에서 설명한 바와 같이 Inotify는 파일 디스크립터를 사용하며 프로그래머는 표준 select 함수나 poll 함수를 사용하여 이벤트를 감시할 수 있다. 그 결과 효과적으로 I/O를 다중화하거나 Glib의 mainloop와 통합할 수 있다. 이와는 대조적으로 dnotify는 때로는 개발자들이 다소 확인하기 어려운 신호를 사용한다.또한, 커널 2.6.25에서는 Signal-drive I.O 알림이 Inotify에 추가되었다.

    Inotify API

    Inotify는 최소한의 파일 디스크립터를 사용하면서 세밀한 모니터링을 할 수 있게 도움을 주는 간단한 API를 제공한다. Inotify와의 통신은 시스템 호출을 통해 설정된다. 사용 가능한 함수는 다음과 같다.

    inotify_init
    이 함수는 inotify 인스턴스를 작성하고 이 인스턴스가 참조하는 파일 디스크립터를 리턴하는 시스템 호출이다.
    inotify_init1
    이 함수는 inotify_init와 비슷하지만 추가 플래그가 있다. 플래그가 지정되지 않으면 이 함수는 inotify_init와 동일하게 작동한다.
    inotify_add_watch
    이 함수는 파일이나 디렉토리에 감시를 추가하며 감시할 이벤트를 지정한다. 기존 감시에 이벤트를 추가해야 하는지, 경로에 디렉토리가 표시되는 경우에만 감시를 수행해야 하는지, 기호 링크를 수행해야 하는지 그리고 감시가 첫 번째 이벤트가 발생하면 중지되어야 하는 일회성 감시인지를 플래그를 통해 제어한다.
    inotify_rm_watch
    이 함수는 감시 목록에서 감시 항목을 제거한다.
    read
    이 함수는 하나 이상의 이벤트에 관한 정보가 포함된 버퍼를 읽는다.
    close
    이 함수는 파일 디스크립터를 닫고 파일 디스크립터에 남아 있는 모든 감시를 제거한다. 인스턴스의 모든 파일 디스크립터가 닫히면 해당 자원과 기본 오브젝트가 해제되며 따라서 커널에서 이러한 자원과 오브젝트를 다시 사용할 수 있게 된다.

    따라서 일반적인 모니터링 프로그램은 다음과 같은 작업을 수행한다.

    1. inotify_init를 사용하여 파일 디스크립터를 연다.
    2. 하나 이상의 감시를 추가한다.
    3. 이벤트를 대기한다.
    4. 이벤트를 처리한 후 대기 상태로 돌아간다.
    5. 더 이상 활성화된 감시가 없거나 어떤 신호를 수신하는 경우에는 파일 디스크립터를 닫고 정리한 후 종료된다.

    다음 섹션에서는 감시할 수 있는 이벤트와 샘플 프로그램에서 이 이벤트가 어떻게 작동하는지를 살펴본다. 마지막에는 이벤트 모니터링이 어떻게 작동하는지 확인한다.


    알림

    애플리케이션이 알림을 읽으면 하나 이상의 이벤트로 구성된 시퀀스가 사용자가 제공한 버퍼로 읽혀진다. Listing 1에 표시된 바와 같이 이벤트는 가변 길이 구조로 리턴된다. 모든 데이터가 버퍼에 채워지면 마지막 항목의 부분 이름이나 부분 이벤트 정보와 같은 사항을 처리해야 한다.


    Listing 1. Inotify의 이벤트 구조
    struct inotify_event
    {
      int wd;               /* Watch descriptor.  */
      uint32_t mask;        /* Watch mask.  */
      uint32_t cookie;      /* Cookie to synchronize two events.  */
      uint32_t len;         /* Length (including NULs) of name.  */
      char name __flexarr;  /* Name.  */
      };

    name 필드는 감시 항목이 디렉토리이고 이벤트가 디렉토리 자체가 아니라 디렉토리에 있는 항목에 해당하는 경우에만 표시된다. IN_MOVED_FROM 이벤트와IN_MOVED_TO 이벤트가 감시할 항목과 관련된 경우에는 두 이벤트의 연관성을 확인하기 위해 쿠키를 사용한다. mask 필드에는 커널에 의해 설정되는 플래그와 함께 이벤트 유형이 리턴된다. 예를 들면, 이벤트 대상이 디렉토리인 경우에는 커널에서 IN_ISDIR 플래그를 설정한다.


    감시할 수 있는 이벤트

    여러 이벤트를 감시할 수 있다. IN_DELETE_SELF와 같은 것은 감시할 항목에만 적용하는 데 반해서 IN_ATTRIB나 IN_OPEN과 같은 것은 감시 항목이나 해당 항목이 디렉토리인 경우에는 디렉토리 안에 있는 디렉토리나 파일에 적용할 수 있다.

    IN_ACCESS
    감시 디렉토리에 있는 감시 항목이 액세스되었다. 예를 들면, 열린 파일이 읽힌 경우
    IN_MODIFY
    감시 디렉토리에 있는 감시 항목이 수정되었다. 예를 들면, 열린 파일이 업데이트된 경우
    IN_ATTRIB
    감시 디렉토리에 있는 감시 항목에서 메타데이터가 변경되었다. 예를 들면, 시간소인이나 사용 권한이 변경된 경우
    IN_CLOSE_WRITE
    쓰기 위해 연 파일이나 디렉토리가 닫혔다.
    IN_CLOSE_NOWRITE
    읽기 전용으로 연 파일이나 디렉토리가 닫혔다.
    IN_CLOSE
    이전의 두 가지 닫기 이벤트(IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)를 논리적 OR 연산을 하는 편리한 마스크이다.
    IN_OPEN
    파일이나 디렉토리가 열렸다.
    IN_MOVED_FROM
    감시 디렉토리에 있는 감시 항목이 감시 위치에서 이동되었다. 또한, 이 이벤트에는 쿠키가 포함되어 있으며 사용자는 이 쿠키를 이용하여IN_MOVED_FROM와 IN_MOVED_TO의 연관성을 확인할 수 있다.
    IN_MOVED_TO
    파일이나 디렉토리가 감시 위치에서 이동되었다. 이 이벤트에는 IN_MOVED_FROM과의 연관성을 확인할 수 있는 쿠키가 포함되어 있다. 파일이나 디렉토리의 이름이 변경되면 두 가지 이벤트가 모두 표시된다. 파일이나 디렉토리가 감시하고 있지 않은 위치로 이동하거나 이 위치에서 이동되는 경우에는 한 가지 이벤트만 표시된다. 사용자가 감시 항목을 이동하거나 이름을 변경하는 경우에도 감시는 계속된다. 아래에 있는IN_MOVE-SELF를 살펴보자.
    IN_MOVE
    이전의 두 가지 이동 이벤트(IN_MOVED_FROM | IN_MOVED_TO)를 논리적 OR 연산을 하는 편리한 마스크이다.
    IN_CREATE
    서브디렉토리나 파일이 감시 디렉토리에서 작성되었다.
    IN_DELETE
    서브디렉토리나 파일이 감시 디렉토리에서 삭제되었다.
    IN_DELETE_SELF
    감시 항목 자체가 삭제되었다. 감시가 종료되고 IN_IGNORED 이벤트를 수신하게 된다.
    IN_MOVE_SELF
    감시 항목 자체가 이동되었다.

    이벤트 플래그 외에도 사용자가 Inotify 헤더(/usr/include/sys/inotify.h)에서 찾을 수 있는 몇 가지 다른 플래그가 있다. 예를 들어, 첫 번째 이벤트만 감시하려고 하는 경우에는 감시를 추가할 때 IN_ONESHOT 플래그를 설정한다.


    간단한 Inotify 애플리케이션

    샘플 애플리케이션(다운로드 섹션 참조)은 위에 있는 일반적인 로직을 따른다. 여기에서는 신호 핸들러를 사용하여 ctrl-c(SIGINT)를 확인한 다음,애플리케이션이 종료되는 것을 알 수 있도록 플래그(keep_running)를 다시 설정한다. 사실상, Inotify 호출은 유틸리티 루틴에서 수행된다. 또한, 기본 Inotify오브젝트에서 이벤트를 지운 다음 나중에 처리할 수 있도록 큐를 작성한다. 실제 애플리케이션에서는 큐를 사용하여 이벤트를 처리하기보다는 다양한 스레드(우선순위가 높은 스레드)에서 이벤트가 처리되기를 원할 수도 있다. 이 애플리케이션에서는 단순히 일반적인 원칙을 설명한다. 여기에서는 이벤트로 구성된 매우 간단한 연결 목록을 사용하며 여기서 각 큐 항목은 원본 이벤트와 큐에 있는 다음 이벤트를 가리키는 포인터를 위한 공간으로 구성된다.

    기본 프로그램

    신호 핸들러와 기본 루틴은 Listing 2에 표시되어 있다. 이 간단한 예제에서는 명령행에서 전달되는 각 파일이나 디렉토리에 감시를 설정하고 이벤트 마스크 IN_ALL_EVENTS를 사용하여 각 파일이나 디렉토리에 대한 모든 이벤트를 감시한다. 실제 애플리케이션에서는 파일 및 디렉토리 작성 이벤트나 삭제 이벤트만을 추적하고자 할 수 있으며 이런 경우에는 속성 변경뿐만 아니라 열기와 닫기 이벤트 마스크를 제거할 수 있다. 파일이나 디렉토리가 이동되거나 이름이 변경되는 것을 추적할 필요가 없는 경우에도 다양한 이동 이벤트 마스크를 제거할 수 있다. 더 자세한 사항은 Inotify man 페이지를 참조한다.


    Listing 2. inotify-test.c의 샘플 기본 루틴
    /* Signal handler that simply resets a flag to cause termination */
    void signal_handler (int signum)
    {
      keep_running = 0;
    }
    
    int main (int argc, char **argv)
    {
      /* This is the file descriptor for the inotify watch */
      int inotify_fd;
    
      keep_running = 1;
    
      /* Set a ctrl-c signal handler */
      if (signal (SIGINT, signal_handler) == SIG_IGN)
        {
          /* Reset to SIG_IGN (ignore) if that was the prior state */
          signal (SIGINT, SIG_IGN);
        }
    
      /* First we open the inotify dev entry */
      inotify_fd = open_inotify_fd ();
      if (inotify_fd > 0)
        {
    
          /* We will need a place to enqueue inotify events,
             this is needed because if you do not read events
             fast enough, you will miss them. This queue is 
             probably too small if you are monitoring something
             like a directory with a lot of files and the directory 
             is deleted.
           */
          queue_t q;
          q = queue_create (128);
    
          /* This is the watch descriptor returned for each item we are 
             watching. A real application might keep these for some use 
             in the application. This sample only makes sure that none of
             the watch descriptors is less than 0.
           */
          int wd;
    
    
          /* Watch all events (IN_ALL_EVENTS) for the directories and 
             files passed in as arguments.
             Read the article for why you might want to alter this for 
             more efficient inotify use in your app.      
           */
          int index;
          wd = 0;
          printf("\n");
          for (index = 1; (index < argc) && (wd >= 0); index++) 
    	{
    	  wd = watch_dir (inotify_fd, argv[index], IN_ALL_EVENTS);
    	}
    
          if (wd > 0) 
    	{
    	  /* Wait for events and process them until a 
             termination condition is detected
     	  */
    	  process_inotify_events (q, inotify_fd);
    	}
          printf ("\nTerminating\n");
    
          /* Finish up by closing the fd, destroying the queue,
             and returning a proper code
           */
          close_inotify_fd (inotify_fd);
          queue_destroy (q);
        }
      return 0;
    }
    

    inotify_init를 사용하여 파일 디스크립터 열기

    Listing 3에는 Inotify 인스턴스를 작성하고 이 인스턴스에 해당하는 파일 디스크립터를 가져오는 데 필요한 간단한 유틸리티 함수가 있다. 이 파일 디스크립터는 호출자에게 리턴된다. 오류가 발생하는 경우에는 리턴값이 음수가 된다.


    Listing 3. inotify_init 사용하기
    /* Create an inotify instance and open a file descriptor
       to access it */
    int open_inotify_fd ()
    {
      int fd;
    
      watched_items = 0;
      fd = inotify_init ();
    
      if (fd < 0)
        {
          perror ("inotify_init () = ");
        }
      return fd;
      }

    inotify_add_watch를 사용하여 감시 추가하기

    Inotify 인스턴스에 해당하는 파일 디스크립터가 있으면 하나 이상의 감시를 추가할 필요가 있다. 해당 마스크를 사용하여 감시할 특정 이벤트를 설정한다. 예제에서는 사용 가능한 모든 이벤트를 감시하는 IN_ALL_EVENTS 마스크를 사용한다.


    Listing 4. inotify_add_watch 사용하기
    int watch_dir (int fd, const char *dirname, unsigned long mask)
    {
      int wd;
      wd = inotify_add_watch (fd, dirname, mask);
      if (wd < 0)
        {
          printf ("Cannot add watch for \"%s\" with event mask %lX", dirname,
    	      mask);
          fflush (stdout);
          perror (" ");
        }
      else
        {
          watched_items++;
          printf ("Watching %s WD=%d\n", dirname, wd);
          printf ("Watching = %d items\n", watched_items); 
        }
      return wd;
    }
                

    이벤트 처리 루프

    이제까지 몇 가지 감시를 설정했으므로 다음에는 이벤트를 대기하는 과정을 살펴본다.일부 감시가 여전히 설정되어 있고 신호 핸들러가 keep_running 플래그를 다시 설정하지 않는 한 루프를 반복한다. 루프는 이벤트가 발생하기를 기다리다가 이벤트가 발생하면 큐로 보낸 다음, 대기 상태로 돌아가 다른 이벤트를 처리하기 전에 큐를 처리한다. 실제 애플리케이션에서는 하나의 스레드에서는 이벤트를 큐로 보내고 다른 스레드에서는 이 이벤트를 처리하게 된다. 이 루프는 Listing 5에 있다.


    Listing 5. 이벤트 처리 루프
    int process_inotify_events (queue_t q, int fd)
    {
      while (keep_running && (watched_items > 0))
        {
          if (event_check (fd) > 0)
    	{
    	  int r;
    	  r = read_events (q, fd);
    	  if (r < 0)
    	    {
    	      break;
    	    }
    	  else
    	    {
    	      handle_events (q);
    	    }
    	}
        }
      return 0;
      }

    이벤트 대기하기

    샘플 애플리케이션은 기한 없이 대기하며 감시 이벤트가 발생하거나 신호에 의해 처리가 중단되는 경우에만 시작된다. 이 코드는 Listing 6에 있다.


    Listing 6. 이벤트나 인터럽트 대기하기
    int event_check (int fd)
    {
      fd_set rfds;
      FD_ZERO (&rfds);
      FD_SET (fd, &rfds);
      /* Wait until an event happens or we get interrupted 
         by a signal that we catch */
      return select (FD_SETSIZE, &rfds, NULL, NULL, NULL);
      }

    이벤트 읽기

    이벤트가 발생하면 대용량 버퍼를 다 채울 만큼 많은 이벤트를 읽은 다음, 이 이벤트를 큐에 저장하여 이벤트 핸들러가 처리할 수 있도록 한다. 샘플 코드에서는 설정된 16,384바이트 버퍼를 다 채우고도 남을 정도로 많은 이벤트가 있는 경우를 처리하지 못한다. 버퍼의 끝에 있는 부분 이벤트를 처리할 수 있어야 한다. 이름의 길이에 대한 현재의 한계가 문제가 되지는 않지만 문제를 사전에 예방하려면 이름으로 인해 버퍼가 오버플로우되지 않도록 확인해야 한다.


    Listing 7. 이벤트를 읽어서 큐로 보내기
    int read_events (queue_t q, int fd)
    {
      char buffer[16384];
      size_t buffer_i;
      struct inotify_event *pevent;
      queue_entry_t event;
      ssize_t r;
      size_t event_size, q_event_size;
      int count = 0;
    
      r = read (fd, buffer, 16384);
      if (r <= 0)
        return r;
      buffer_i = 0;
      while (buffer_i < r)
        {
          /* Parse events and queue them. */
          pevent = (struct inotify_event *) &buffer[buffer_i];
          event_size =  offsetof (struct inotify_event, name) + pevent->len;
          q_event_size = offsetof (struct queue_entry, inot_ev.name) + 
                                      pevent->len;
          event = malloc (q_event_size);
          memmove (&(event->inot_ev), pevent, event_size);
          queue_enqueue (event, q);
          buffer_i += event_size;
          count++;
        }
      printf ("\n%d events queued\n", count);
      return count;
    }

    이벤트 처리

    마침내 처리할 이벤트가 몇 개 발생했다. 이 애플리케이션에서는 어떤 이벤트가 발생했는지 간단히 보고한다. 이벤트 구조에 이름이 있으면 그것이 파일이나 디렉토리 중 어느 것인지 보고한다. 또한, 파일이나 디렉토리가 이동한 경우에는 사용자가 이동이나 이름 바꾸기 이벤트의 연관성을 확인할 수 있도록 쿠키 정보를 보고한다. Listing 8에는 코드의 일부가 있으며 이 코드에는 일부 이벤트를 처리하는 코드가 포함되어 있다. 완전한 코드는 다운로드 섹션을 참조한다.


    Listing 8. 이벤트 처리
    void handle_event (queue_entry_t event)
    {
      /* If the event was associated with a filename, we will store it here */
      char *cur_event_filename = NULL;
      char *cur_event_file_or_dir = NULL;
      /* This is the watch descriptor the event occurred on */
      int cur_event_wd = event->inot_ev.wd;
      int cur_event_cookie = event->inot_ev.cookie;
    
      unsigned long flags;
    
      if (event->inot_ev.len)
        {
          cur_event_filename = event->inot_ev.name;
        }
      if ( event->inot_ev.mask & IN_ISDIR )
        {
          cur_event_file_or_dir = "Dir";
        }
      else 
        {
          cur_event_file_or_dir = "File";
        }
      flags = event->inot_ev.mask & 
        ~(IN_ALL_EVENTS | IN_UNMOUNT | IN_Q_OVERFLOW | IN_IGNORED );
    
      /* Perform event dependent handler routines */
      /* The mask is the magic that tells us what file operation occurred */
      switch (event->inot_ev.mask & 
    	  (IN_ALL_EVENTS | IN_UNMOUNT | IN_Q_OVERFLOW | IN_IGNORED))
        {
          /* File was accessed */
        case IN_ACCESS:
          printf ("ACCESS: %s \"%s\" on WD #%i\n",
    	      cur_event_file_or_dir, cur_event_filename, cur_event_wd);
          break;
    
          /* File was modified */
        case IN_MODIFY:
          printf ("MODIFY: %s \"%s\" on WD #%i\n",
    	      cur_event_file_or_dir, cur_event_filename, cur_event_wd);
          break;
    
          /* File changed attributes */
        case IN_ATTRIB:
          printf ("ATTRIB: %s \"%s\" on WD #%i\n",
    	      cur_event_file_or_dir, cur_event_filename, cur_event_wd);
          break;
    
          /* File open for writing was closed */
        case IN_CLOSE_WRITE:
          printf ("CLOSE_WRITE: %s \"%s\" on WD #%i\n",
    	      cur_event_file_or_dir, cur_event_filename, cur_event_wd);
          break;
    
          /* File open read-only was closed */
        case IN_CLOSE_NOWRITE:
          printf ("CLOSE_NOWRITE: %s \"%s\" on WD #%i\n",
    	      cur_event_file_or_dir, cur_event_filename, cur_event_wd);
          break;
    
          /* File was opened */
        case IN_OPEN:
          printf ("OPEN: %s \"%s\" on WD #%i\n",
    	      cur_event_file_or_dir, cur_event_filename, cur_event_wd);
          break;
    
          /* File was moved from X */
        case IN_MOVED_FROM:
          printf ("MOVED_FROM: %s \"%s\" on WD #%i. Cookie=%d\n",
    	      cur_event_file_or_dir, cur_event_filename, cur_event_wd, 
                  cur_event_cookie);
          break;
    .
    . (other cases)
    .
          /* Watch was removed explicitly by inotify_rm_watch or automatically
             because file was deleted, or file system was unmounted.  */
        case IN_IGNORED:
          watched_items--;
          printf ("IGNORED: WD #%d\n", cur_event_wd);
          printf("Watching = %d items\n",watched_items); 
          break;
    
          /* Some unknown message received */
        default:
          printf ("UNKNOWN EVENT \"%X\" OCCURRED for file \"%s\" on WD #%i\n",
    	      event->inot_ev.mask, cur_event_filename, cur_event_wd);
          break;
        }
      /* If any flags were set other than IN_ISDIR, report the flags */
      if (flags & (~IN_ISDIR))
        {
          flags = event->inot_ev.mask;
          printf ("Flags=%lX\n", flags);
        }
        }

    이 간단한 예제는 Inotify가 어떻게 작동하고 사용자가 어떤 이벤트를 감시할 수 있는지 나타나도록 디자인되었다. 필요에 따라 감시할 이벤트와 이 이벤트를 처리할 방식을 정할 수 있다.


    예제 사용법

    이 섹션에서는 디렉토리에 파일이 있는 두 가지 레벨의 간단한 디렉토리 구조를 작성한 다음, 샘플 애플리케이션을 실행하여 Inotify를 통해 모니터할 수 있는 몇 가지 이벤트를 설명한다. 터미널 세션에서 Inotify 샘플 프로그램을 시작하겠지만 이 프로그램의 결과물에 명령이 삽입될 수 있도록 &를 사용하여 백그라운드에서 실행한다.또한, 하나 이상의 다른 창에서 명령을 실행하면서 하나의 터미널 창에서 이 프로그램을 실행할 수도 있다. Listing 9에는 샘플 디렉토리 구조와 빈 파일을 작성하는 과정이 샘플 프로그램을 처음 시작할 때 발생하는 결과물과 함께 표시되어 있다.


    Listing 9. 샘플 환경 작성
    ian@attic4:~/inotify-sample$ mkdir -p dir1/dir2
    ian@attic4:~/inotify-sample$ touch dir1/dir2/file1
    ian@attic4:~/inotify-sample$ ./inotify_test dir1/ dir1/dir2/ dir1/dir2/file1&
    [2] 8733
    ian@attic4:~/inotify-sample$ 
    Watching dir1/ WD=1
    Watching = 1 items
    Watching dir1/dir2/ WD=2
    Watching = 2 items
    Watching dir1/dir2/file1 WD=3
    Watching = 3 items
    
    ian@attic4:~/inotify-sample$ 
                

    Listing 10에는 dir2의 내용이 표시되어 있다. 첫 번째 이벤트는 dir1에 대한 보고 내용이며 감시 디스크립터 1에서 감시되고 있는 디렉토리인 dir2가 열려 있다고 표시되어 있다. 두 번째 항목은 감시 디스크립터 2에 대한 내용이며 감시되고 있는 항목(이 경우에는 dir2)이 열려 있다고 표시되어 있다. 디렉토리 트리에 있는 많은 항목을 감시하는 경우에는 이러한 형태의 결과물이 이중으로 표시될 때도 있다.


    Listing 10. dir2의 내용 표시
    ian@attic4:~/inotify-sample$ ls dir1/dir2
    file1
    
    4 events queued
    OPEN: Dir "dir2" on WD #1
    OPEN: Dir "(null)" on WD #2
    CLOSE_NOWRITE: Dir "dir2" on WD #1
    CLOSE_NOWRITE: Dir "(null)" on WD #2

    Listing 11에서는 file1에 약간의 텍스트를 추가한다. 이 파일과 이 파일을 포함하고 있는 디렉토리에 대한 이중의 열기, 닫기 및 수정 이벤트에 다시 한 번 주목하자. 또한, 한 번에 모든 이벤트가 읽히지 않았다는 점에 유념하자. 이 애플리케이션의 큐잉 루틴은 비동기적으로 세 번 호출되며 매번 두 개의 이벤트를 사용한다. 다시 애플리케이션을 실행하여 동일한 작업을 매번 반복하면 이러한 특수한 작동이 반복될 수 있으며 또는 그렇지 않을 수도 있다.


    Listing 11. file1에 텍스트 추가
    ian@attic4:~/inotify-sample$ echo "Some text" >> dir1/dir2/file1
    
    2 events queued
    OPEN: File "file1" on WD #2
    OPEN: File "(null)" on WD #3
    
    2 events queued
    MODIFY: File "file1" on WD #2
    MODIFY: File "(null)" on WD #3
    
    2 events queued
    CLOSE_WRITE: File "file1" on WD #2
    CLOSE_WRITE: File "(null)" on WD #3

    Listing 12에서는 file1의 속성을 변경한다. 그러면 감시 항목과 이 항목을 포함하고 있는 디렉토리에 대한 이중 결과물이 다시 한 번 표시된다.


    Listing 12. 파일 속성 변경
    ian@attic4:~/inotify-sample$ chmod a+w dir1/dir2/file1
    
    2 events queued
    ATTRIB: File "file1" on WD #2
    ATTRIB: File "(null)" on WD #3

    이제 디렉토리 레벨을 높여서 file1을 dir1으로 이동시키자. 해당 결과물은 Listing 13에 표시되어 있다. 이번에는 항목이 이중으로 표시되지 않았다. 실제로 각 디렉토리와 파일 자체에 하나씩 세 개의 항목이 표시되었다.MOVED-FROM 이벤트와 MOVED_TO 이벤트의 연관성을 확인할 수 있도록 쿠키(569)가 표시되었다.


    Listing 13. file1을 dir1으로 이동
    ian@attic4:~/inotify-sample$ mv dir1/dir2/file1 dir1
    
    3 events queued
    MOVED_FROM: File "file1" on WD #2. Cookie=569
    MOVED_TO: File "file1" on WD #1. Cookie=569
    MOVE_SELF: File "(null)" on WD #3

    이제, file1에서 file2로 하드 링크를 작성하자. 해당 inode에 대한 링크 번호가 변경되기 때문에 file1에서는 ATTRIB 이벤트가 발생하고file2에서는 CREATE 이벤트가 발생한다.


    Listing 14. 하드 링크 작성
    ian@attic4:~/inotify-sample$ ln dir1/file1 dir1/file2
    
    2 events queued
    ATTRIB: File "(null)" on WD #3
    CREATE: File "file2" on WD #1

    이제, file1을 현재의 디렉토리로 이동시킨 다음, 이름을 file3로 변경한다. 현재의 디렉토리는 감시하고 있지 않으므로 MOVED_FROM 이벤트와 연관되는 MOVED_TO 이벤트는 발생하지 않는다.


    Listing 15. file1을 감시하지 않는 디렉토리로 이동하기
    ian@attic4:~/inotify-sample$ mv dir1/file1 ./file3
    
    2 events queued
    MOVED_FROM: File "file1" on WD #1. Cookie=572
    MOVE_SELF: File "(null)" on WD #3

    이 시점에서는 dir2가 비어 있으므로 이 디렉토리는 제거한다. 감시 디스크립터 2에 대해 IGNORED 이벤트가 발생하며 따라서 이제는 두 개의 항목만 감시 상태에 있게 된다.


    Listing 16. dir2 제거
    ian@attic4:~/inotify-sample$ rmdir dir1/dir2
    
    3 events queued
    DELETE: Dir "dir2" on WD #1
    DELETE_SELF: File "(null)" on WD #2
    IGNORED: WD #2
    Watching = 2 items

    file3를 제거하자. 이번에는 IGNORED 이벤트가 발생하지 않는다. 왜 그럴까? 그리고 왜 file3(이 파일은 원래 dir1/dir2/file1이었음)에 대해 ATTRIB 이벤트가 발생하지 않을까?


    Listing 16. file3 삭제
    ian@attic4:~/inotify-sample$ rm file3
    
    1 events queued
    ATTRIB: File "(null)" on WD #3

    file1에서 file2로 하드 링크를 작성했다는 점을 기억하자. 애플리케이션을 시작했을 때 file2가 없었음에도 감시 디스크립터 3에서 여전히 file2를 감시하고 있다는 것을 Listing 17에서 확인할 수 있다.


    Listing 17. file2가 여전히 감시되고 있다!
    ian@attic4:~/inotify-sample$ touch dir1/file2
    
    6 events queued
    OPEN: File "file2" on WD #1
    OPEN: File "(null)" on WD #3
    ATTRIB: File "file2" on WD #1
    ATTRIB: File "(null)" on WD #3
    CLOSE_WRITE: File "file2" on WD #1
    CLOSE_WRITE: File "(null)" on WD #3

    그러면 이제 dir1을 삭제한다. 그리고 다수의 이벤트를 감시하다가 결국에는 더 이상 감시할 대상이 없어서 프로그램이 스스로 종료하도록 하자.


    Listing 18. dir1 삭제
    ian@attic4:~/inotify-sample$ rm -rf dir1
    
    8 events queued
    OPEN: Dir "(null)" on WD #1
    ATTRIB: File "(null)" on WD #3
    DELETE_SELF: File "(null)" on WD #3
    IGNORED: WD #3
    Watching = 1 items
    DELETE: File "file2" on WD #1
    CLOSE_NOWRITE: Dir "(null)" on WD #1
    DELETE_SELF: File "(null)" on WD #1
    IGNORED: WD #1
    Watching = 0 items
    
    Terminating


    Inotify의 이용 가능성

    Inotify를 다양한 목적으로 사용할 수 있다. 다음은 몇 가지 사례이다.

    성능 모니터링
    사용자가 어떤 파일과 애플리케이션이 가장 자주 열리는지 판별하고자 할 수 있다. 작은 파일이 반복적으로 열리고 닫힌다는 사실을 발견하면 이러한 데이터가 다른 방식으로 공유되도록 인메모리 버전을 사용하거나 애플리케이션을 변경하는 것을 고려해 볼 수 있다.
    메타 정보
    파일을 마지막으로 수정한 사람의 ID나 원래의 작성 시간과 같은 파일에 관한 추가 정보를 로그하고자 할 수도 있다.
    보안
    보안을 이유로 특정 파일이나 디렉토리에 대한 모든 액세스 권한을 모니터하고자 할 수도 있다.

    샘플 코드는 모든 이벤트를 감시하고 보고한다. 실제로는 필요에 따라 이벤트의 특정 서브세트만을 확인하고자 할 수도 있다. 또한, 다양한 감시 항목에서 다양한 이벤트를 감시할 수도 있다. 예를 들면, 파일에서는 열기와 닫기 이벤트를 감시하고 디렉토리에서는 작성이나 삭제 이벤트만을 감시하고자 할 수도 있다. 가능하면 관심 있는 최소한의 이벤트만을 감시하도록 기능을 제한해야 한다.


    결론

    성능 모니터링, 디버깅 및 자동화와 같은 영역에 적용하는 경우, Inotify는 Linux 파일 시스템을 모니터링 할 수 있는 매우 세분화된 강력한 메커니즘이 된다. 이 기사에서 제공된 샘플 코드를 사용하여 성능상의 오버헤드를 최소로 하면서 파일 시스템 이벤트를 실시간으로 기록하거나 이 이벤트에 응답하는 자체 애플리케이션을 작성해 보자.



    다운로드 하십시오

    설명이름크기다운로드 방식
    Sample code used in this articleinotify-sample.tgz4 KBHTTP

    다운로드 방식에 대한 정보


    참고자료

    교육

    제품 및 기술 얻기

    • 자신에게 가장 적합한 방법으로 IBM제품을 평가해 보자. 시험판 제품을 다운로드하거나, 온라인으로 제품을 사용해 보거나, 클라우드 환경에서 제품을 사용하거나,SOA Sandbox에서SOA(Service Oriented Architecture)를 효과적으로 구현하는 방법을 배울 수 있다.

    토론

    • developerWorks community에 참여하자.개발자 중심 블로그, 포럼, 그룹 및 Wiki 검색 중에 다른 developerWorks 사용자와 의견을 교환해 보자.

    필자소개

    Ian Shields 사진

    Ian Shields는 developerWorks 리눅스 영역을 위한 리눅스 프로젝트 다수를 수행하고 있다. Shields는 노스 캐롤라이나 주 소재 IBM 리서치 트라이앵글 파크에서 선임 프로그래머로 일한다. Shields는 1973년 시스템 엔지니어로 오스트레일리아, 캔베라에 있는 IBM 사무실에 들어갔으며 캐나다 몬트리얼과 노스 캐롤라이나 주 RTP에서 통신 시스템과 배포 컴퓨팅 부문에서 일해왔다. Shields는 특허 여러 건을 획득했으며, 논문 여러 건을 발표했다. Shields는 순수 수학과 철학 학사 학위를 오스트레일리안 국립 대학에서 받았다. 노스 캐롤라이나 주립 대학에서 컴퓨터 과학 분야를 대상으로 석사와 박사 학위를 받았다. 전자편지 주소는 ishields@us.ibm.com이다.

    크리에이티브 커먼즈 라이센스
    Creative Commons License
    2011/06/16 01:02 2011/06/16 01:02
    받은 트랙백이 없고, 댓글이 없습니다.

    댓글+트랙백 RSS :: http://blog.hwport.com/rss/response/624

    댓글+트랙백 ATOM :: http://blog.hwport.com/atom/response/624


    본 문서는 "http://blog.naver.com/p1ngp1ng?Redirect=Log&logNo=120049518578" 에서 발췌한 것임을 밝힙니다.




     미들웨어(Home Middle-Ware)

     

     네트워크에 연결된 각종 시스템들의 물리적인 위치, 프로토콜, 운영 체계(OS) 등에 관계없이 통합 시스템으로서의 연동을가능하게 하는 중간 매개 프로그램.  네트워크 환경 내에서 다양한 하드웨어, 네트워크 프로토콜, 응용 프로그램, PC 환경 OS 차이를 메워 주고, 복잡한 이기종 환경에서 응용 프로그램과 OS 간의 원만한 통신을 이룰  있게  준다. 대표적인 네트워크 미들웨어로는 Jini, LonWorks, HAVi, UPnP 등을   있다.

     

     미들웨어의 특징

    l  액세스 망에 대해  종결(Network Termination) 모뎀 기능을 제공

    l  다양한  네트워크를 연결하여 망간 연동기능을 제공하는 가정용 게이트웨이

    l  액세스  네트워크(xDSL, 케이블망, 위성, FTTx) 연동기술, 네트워크 주소변환기술,  네트워크 프로토콜 변환 기술, 네트워크 보안 기술 등이 포함 .

     

     미들웨어 종류

    1.     UPnP(Universal Plug and Play)

    마이크로소프트사가 제안한 미들웨어 솔루션으로서 기존의 IP 네트워크와 HTTP 프로토콜을 사용하여  네트워크 기기간의 제어와 상호운용을 목표로 하고 있다.  기술에 의한 기기간 제어모델을 용이하게 구현함으로써 H/W S/W  OS 무관하게동작이 가능하고, HTML 이용하므로 손쉬운 사용자 인터페이스를 제공하는 장점을 갖고 있다.

    네트워크 접속 기기 간의 데이터 공유 기능을 위해 IPP(Internet Printing Protocol) 같은 새로운 프로토콜을 사용함으로써PC 중심으로  가정  각종 가전기기를 제어할  있다. 또한 ISA, PCI, VESA, USB 모든 인터페이스와 네트워크(IP) 지원하며, Ethernet, Home RF, Home PNA 등의 네트워크 프로토콜에서도 적용이 가능하다.

    UPnP 가정이나 작은 사무실과 같이 관리자가 없는 네트워크 환경에서 사용자의 작업이 없이 표준화된 방법으로 쉽게 장비간의 연결이나 장비와 인터넷의 연결 방안을 제공한다. ,  장비들은 언제나 쉽게 네트워크에 접속을 하여 다른 장비들에게 자신의 기능을 알리고 통신을 하거나 제어를   있도록 해주며,  이상 사용하지 않을 경우에는 네트워크에서 쉽게 제거 시킬 있도록 해준다.

    UPnP 장비간의 11 통신(Peer-to-Peer) 기반으로 하고 있으며 현존하는 Internet 표준 프로토콜(TCP/IP) 이용하여 구조를 정의하고 있다. 그러나, 이로 인해 장비마다 IP 부여하고 동적으로 IP 할당 받아야 하기 때문에 DHCP(자동으로IP 주소를 할당하는 기능) 같이 가정내의 장비의 동적인 IP 할당방안을 제공하거나 IP 주소의 크기를 늘인 IPv6 필요로 한다는 점은 UPnP 확산에 부담으로 작용하고 있다. UPnP 모임은 MS, Intel 주축으로 하여 정보, 가전중심의 250 이상의 회사들이 회원으로 가입이 되어 있으며 현재 활발한 활동을 진행 중이다.

    UPnP 특징

    l  소규모에서 대규모의 네트워크로 확장이 용이

    l  PnP 지원하여 장비의 접속과 분리를 자동으로 인지

    l  개발이 용이

    l  작은 리소스로도 이용이 가능

    l  가전장비와 같이 IP 없는 장비에 대해서는 단순한 기능을 가진 SCP라는 프로토콜을 통하여 브릿지(Bridge; 네트워크프로토콜 변환기) 연결할  있도록 지원

    l  Mixed-media, multi-vender home network 환경의 OS, 언어  하드웨어 독립적 환경 구축 가능

    l  인터넷 중심의 기술

    l  HTTP, XML, multicast, UDP, SOAP, JAVA  다양한 운영체계에서 사용가능

     

    UPnP 구조

    UPnP 장비에 관계없이 공통적인 Interface 제공한다. 따라서  서비스용 응용 프로그램 입장에서는 UPnP 지원하는 장비들의 구체적인 사항에 대한 고려를 하지 않아도 통신이 가능하며, 장비의 입장에서도 UPnP만을 지원하면 이를 지원하는 모든서비스용 응용 프로그램과 연결이 가능하게 된다.

    l  SSDP(Simple Service Discovery Protocol) 네트워크에 연결되어 있는 장비와 사용 가능한 서비스를 검색하기 위한프로토콜

    l  Mini Web Server 장비간의 기능  상태정보를 주고 받기 위해서 사용

    l  GENA 장비간의 이벤트(상태 변화 ) 교환을 위해서 사용

    l  SOAP XML 문법을 이용하여 장비에 제어 명령 등을 전달할  사용된다.

     

    UPnP Networking 단계

    l  주소지정(Addressing) : UPnP IP기반의 네트워크이므로 장비들마다 IP 필요하며 이를 위해 IP 할당이 제일 먼저수행 된다. 장비가 처음 네트워크에 접속할  DHCP서버를 검색하여 IP 할당 받으며, 이때  장비들은 모두 DHCP Client 된다.

    l  장비 검색(Discovery) : 주소지정을 통해  장비들이 IP 부여 받게 되면,  다음에는 제어하고자 하는 장비들을 검색하는 것이 필요하다. 이를 위해 제어기에서는 SSDP라는 프로토콜을 사용하여 장비를 검색한다. 제어기는 관심 있는 장비들을 검색하고 피제어기는 이에 응답한다. 피제어기에서는 자신이 네트워크에 접속하는 경우, 자동으로  장비들에게 접속된 사실을 알려주고 주기적으로 접속의 지속여부를 알려준다.

    l  장비 명세(Description) : 장비를 발견하게 되면  장비마다 수행할 서비스가 무엇이 있는가를 알아야 한다. 이를 위해제어기에서 피제어기를 발견하면, 피제어기에서는 장비에 대한 명세가 들어 있는 URL 보내고, 제어기는 피제어기에서 XML 문서로  장비 명세를 가져온다.  문서에는 제조사 정보, 제품정보(모델, 시리얼 번호,…), 서비스 목록 등이담겨있다.

    l  제어(Control) : 제어기는 피제어기에서 장비 명세를 가져와 기술되어 있는 장비의 서비스를 분석한 , 피제어기에게 적절한 명령어(action) 보내어 제어한다.   사용되는 프로토콜은 XML/SOAP이다.

    l  이벤트 처리(Eventing) :  네트워크에서는 주변 환경에 따라서 장비의 상태가 변하는 경우가 빈번하다. 때로는 이러한변화가 사용자에게는 중요한 정보가  수도 있기 때문에 UPnP에서는 이벤트를 정의하고 있다. 제어기는 피제어기의상태가 변화하는 것에 주목을 하고 있고, 피제어기는 자신의 상태가 변할  제어기에게 이벤트 메시지를 전달한다. 이벤트는 (이름, ) 쌍으로 되어 있으며, 이벤트에서 사용되는 프로토콜은 XML형식의 GENA라는 프로토콜이다.

    l  장비의 사용자 인터페이스(Presentation) : 제어기에서는 피제어기의 HTML Page 읽어 들일  있다.  HTML Page 장비 사용에 관련된 사용자 인터페이스를 보여주며, 이를 통하여 장비를 제어하거나 상태를 보여주기도 한다.

     

    2.     Jini

    Java 기반으로 하여 LAN, xDSL, 모뎀, 전력선, 무선  다양한 통신방식으로 접속된 가정내 디지털 장비나 S/W 동적으로상호 작용하도록 하는 기술로서 Sun Microsystems사가 제안하였다. 단순성과 고신뢰성의 확보, 효율적인 제어구조를 지향한확장성 부여 등을 특징으로 하며, P&P 기능에 의한 간단한 시스템 구성과 실행 코드의 이동성에 의한 가변성, 그리고 기존 IP기반으로 하는 네트워크 확장성  Java 연관 제품과 시스템간 호환성 확보 등이 장점이다. 그러나 느린 수행속도와 과다한 메모리 용량 차지 등으로 시스템 단가가 높아진다는 단점도 지니고 있다.

    JINI Sun Microsystems에서 제안할 당시에는  네트워크 시장을 염두에  기술이었으나, 현재는  시장에서보다는 오히려 서버 application 시장에서 영향력을 얻고 있다.

    JINI JAVA 장점인 VM 통해  같은 프로그램을 별도의 수정이나 사용자 개입 없이도 임의의 기기에서 동일한 실행결과를얻을  있다는 점을 네트웍을 통해서 확장시킨 것으로   있다.

    JINI system 사용자들과  사용자들이 이용하려고 하는 장비와 같은 자원의 유기적인 결합(YES federation, NO collection) 지원하는 분산 시스템이다. JINI 역할은 UPnP처럼 네트워크를 통해 사람이나, 기기, 프로그램이 특정 자원을찾거나 사용하고자  , 관리자의 개입 없이 유연하게 동작하는 것을 목표로 한다. 그러나 자원으로 참여하기 위한 조건과 자원을 다루는 방법에서는 다른 유사기술과 확연한 차이를 가지고 있는데, 무엇보다 JINI 자원은 JAVA machine 이어야 한다는점이다. 아마도 이점이 JINI 확산에 가장  걸림돌일 것이다. 점차 고가가전이나 정보가전제품에 JAVA VM 내장하는 경우가 늘어나고는 있지만 여전히 소수에 불과하다. 물론 JAVA VM 없거나 아예 컴퓨팅 파워가 없는 장비가 JINI 네트워크에 참여하는 방법도 마련되어 있으나 이것은