2020년 9월 12일 토요일

Sendmail로 자동 이메일 보내기

이 글은 웹서버에서 프로세스 실행 완료하면 이메일 보내는 방법을 간략히 설명한다. 


Sendmail 설치 및 설정
다음과 같이 sendmail을 설치한다.
$ sudo apt-get install sendmail

다음과 같이 dnl 문자로 주석을 설정한다. 
$ sudo vi /etc/mail/sendmail.mc
FEATURE(`no_default_msa')dnl
dnl DAEMON_OPTIONS(`Family=inet6, Name=MTA-v6, Port=smtp, Addr=::1')dnl
DAEMON_OPTIONS(`Family=inet,  Name=MTA-v4, Port=smtp, Addr=0.0.0.0')dnl
dnl DAEMON_OPTIONS(`Family=inet6, Name=MSP-v6, Port=submission, M=Ea, Addr=::1')dnl
dnl DAEMON_OPTIONS(`Family=inet,  Name=MSP-v4, Port=submission, M=Ea, Addr=0.0.0.0')dnl

$ sudo vi /etc/mail/access 
Connect:localhost.localdomain REPLAY
Connect:localhost RELAY
Connect:127.0.0.1 RELAY
Connect:192.168.0 REPLAY
Connect:test.com REPLAY

$ sudo vi /etc/mail/local-host-names
google.com
kict.re.kr
naver.com
daum.net
localhost
localhost.localdomain
ktw-desktop

다음과 같이 실행하고, 텔넷 등으로 접속한다. Sendmail이 출력되면 성공한 것이다.
$ sudo service sendmail restart
$ telnet localhost 25
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
220 ubuntu ESMTP Sendmail 8.14.4/8.14.4/Debian-2ubuntu2.1; Fri, 10 Mar 2017 10:29:07 +0200; (No UCE/UBE) logging access from: localhost(OK)-localhost [127.0.0.1]


속도가 느리면 다음과 같이 설정한다. hostname은 hostnamectl 명령을 통해 얻는다.
$ sudo vi /etc/hosts 
127.0.0.1  localhost localhost.localdomain hostname

gmail 로 전송되는 경우, 스팸으로 처리된다. 이 경우, gmail 에서 특정 메일주소로 받은 메일을 스팸메일 예외처리하면 된다. 예외처리를 위해 필터 설정해, 스팸을 해제하면 된다. 

이제 다음과 같이 실행해 본다.
echo "Subject: sendmail test" | sendmail -v emailaddress@gmail.com

Sendmail 활용 파이썬 코딩
다음과 같이 파이썬 코딩한다.
from email.mime.text import MIMEText
from subprocess import Popen, PIPE

def send_email(to):
    msg = MIMEText("Finished image capture")
    msg["From"] = "pcdnet@tk"
    msg["To"] = "laputa999@gmail.com"
    msg["Subject"] = "Scan image capture"
    p = Popen(["/usr/sbin/sendmail", "-t", "-oi"], stdin=PIPE)
    p.communicate(msg.as_string())

이제 실행해 보면, 지메일에 메시지가 전송될 것이다.

Appendix - 프로세스 호출

우선 웹서버에서 특정 이벤트가 발생했을 때, 이멜을 보낼려면 프로세스 호출에 대해 이해해야 한다. 프로세스 상태는 다음과 같다. 이때마다 시그널 콜백 함수를 이용해 이벤트를 받을 수 있다.

 
 

 

Appendix - 프로세스 시그널

프로세스가 종료할 때 등 이벤트가 발생하면, 시그널 콜백 함수에서 이를 확인할 수 있다.

signal.signal(signal.SIGHUP,CloseAll)
signal.signal(signal.SIGCHLD,CloseAll)
signal.signal(signal.SIGALRM,CloseAll)
 

다음은 간단한 시그널 예제이다.
from time import sleep
import signal
import sys

def sigterm_handler(_signo, _stack_frame):
    # Raises SystemExit(0):
    sys.exit(0)

if sys.argv[1] == "handle_signal":
    signal.signal(signal.SIGTERM, sigterm_handler)

try:
    print "Hello"
    i = 0
    while True:
        i += 1
        print "Iteration #%i" % i
        sleep(1)
finally:
    print "Goodbye"

#---
import signal
import time

class GracefulKiller:
  kill_now = False
  def __init__(self):
    signal.signal(signal.SIGINT, self.exit_gracefully)
    signal.signal(signal.SIGTERM, self.exit_gracefully)

  def exit_gracefully(self,signum, frame):
    self.kill_now = True

if __name__ == '__main__':
  killer = GracefulKiller()
  while not killer.kill_now:
    time.sleep(1)
    print("doing something in a loop ...")

  print("End of the program. I was killed gracefully :)")

Appendix - 예제

이 예제는 roscore를 자동으로 실행하고, rostopic을 실행한 후 10초 뒤에  kill하는 프로그래이다.

import subprocess
from time import sleep, time
import os, signal

def proc_ros():
    process = subprocess.Popen(['roscore'])
    print('1. process', process.pid)
    # process.wait(10)
    sleep(10)

def proc_roslaunch():
    process = subprocess.Popen(['roslaunch', 'rplidar_ros', 'rplidar.launch'])
    print('2. process', process.pid)
    #process.wait(10)
    sleep(10)

def term_proc():
    process = subprocess.Popen(['rostopic', 'echo', '/scan'])
    print('3. process', process.pid)
    sleep(10)
    for i in range(10):
        sleep(2)
        process.kill()
        print('Timed out - killing', process.pid)

def check_kill_process(pstring):
    for line in os.popen("ps ax | grep " + pstring + " | grep -v grep"):
        fields = line.split()
        pid = fields[0]
        os.kill(int(pid), signal.SIGKILL)

proc_ros();
proc_roslaunch();
term_proc()
sleep(10)
term_proc()
sleep(10)
print('End program')

레퍼런스

댓글 없음:

댓글 쓰기