Thursday, July 29, 2010

Automate Command-line Programs

I had to automate a command-line program. Then I did this script like a sample that do auto control to vi.

This way is possible control any kind of command-line program.

#!/bin/python

import subprocess
import time
import threading

class ProcessRunner(threading.Thread):
def run(self):
self.process = subprocess.Popen(["vi /tmp/xpto > /tmp/viOutput"], shell = True, bufsize = 1000, stdout = subprocess.PIPE, stdin = subprocess.PIPE, stderr = subprocess.STDOUT)

pr = ProcessRunner()
pr.start()

time.sleep(1)

pr.process.stdin.write("i\n")
pr.process.stdin.flush()

time.sleep(1)

pr.process.stdin.write("Content xpto... :P\n")
pr.process.stdin.flush()

time.sleep(1)

pr.process.stdin.write(chr(27))
pr.process.stdin.flush()

time.sleep(1)

pr.process.stdin.write(":wq\n")
pr.process.stdin.flush()


If is necessary read some content from program output, you can put the output in a temp file, in this case goes to /tmp/viOutput. See with attention the line of the subprocess.Popen. Another way is use the pr.process.stdout but it dont work fine for me.

The command pr.process.stdin.write(chr(27)) write the "character" ESC, like the keyboard key.

Thursday, February 4, 2010

NC (NetCast) vs Printers (Impressoras)

Sobrou para mim configurar as novas impressoras Samsung SCX-6345N no Solaris, e como é óbvio e uma grande pena não tem drivers, ou pelo menos que funcione bem, pois apesar de haver um driver PPD supostamente para o LP em Solaris, não funcionou nem a pau.

Fiz tudo o possível para fazer funcionar estas impressoras em Solaris via LP, pois era uma situação de urgência fazer o sistema da Reuters (Kondor) utilizar as novas impressoras.

Após muitas tentativas e alguns e-mails trocados com o suporte da Samsung, e até um programa da Samsung para instalar a impressora em modo gráfico (X11) que "configuraria automaticamente" o LP, também não funcionou. E após também de muitas folhas de erros e cabeçalho de jobs terem sido impressos sem nunca sair o conteúdo desejado. Após tudo isto e um pouco mais... o suporte da Samsung recomendou eu usar o comando:

# cat testprint.ps | nc -w 2 192.168.193.191 9100


Fiquei sético, mas como não havia muito mais a fazer, lá fui eu experimentar isto, e para a minha surpresa... não é que funcionou!

Por acaso eu já tinha pensado nisto, em abrir uma conexão com a impressora direto e mandar o conteúdo PostScript e ver se ela imprimia ou se ficava maluca. Mas não sabia que existia um comando simples para isto.

O comando NC (NetCast) não vem no Solaris 10, por isso fui ao Sunfreeware.com pegar o pacote:

http://www.sunfreeware.com/programlistsparc10.html#nc

Depois é só instalar o pacote:

#pkgadd -d nc-110-sol10-sparc-local.gz


Na configuração da impressora no Kondor (Reuters) ficou apenas assim:

nc -w 2 192.168.193.191 9100


Portanto é executar o nc passando o IP e a PORTA da impressora. O -w 2 apenas quer dizer que tem o timeout de 2 segundos.

A desvantagem desta solução é que perde-se o controle das impressoras com o LP, ou seja, para o Solaris é como se elas não existissem. Então a administração LP e a configuração das impressoras no /etc/lp e os comandos ("lpstat -a", "enable PRINTER" e "disable PRINTER"), tudo isto passa a servir para nada! :D

Como para nos a solução do NC (NetCast) é suficiente, passamos a trabalhar assim.

NC (NetCast)

netcat is a simple unix utility which reads and writes data across network connections, using TCP or UDP protocol. It is designed to be a reliable "back-end" tool that can be used directly or easily driven by other programs and scripts. At the same time, it is a feature-rich network debugging and exploration tool, since it can create almost any kind of connection you would need and has several interesting built-in capabilities. Netcat, or "nc" as the actual program is named, should have been supplied long ago as another one of those cryptic but standard Unix tools.

http://www.computerhope.com/unix/nc.htm

Esta solução de imprimir diretamente para uma impressora de rede usando o NC (NetCast) também é válida para Linux e Unix em geral.

Tuesday, October 20, 2009

Solaris - PDF Printer

Precisei encarar este desafio de configurar uma impressora de PDF em Solaris, para receber a impressão de documentos em PostScript apartir do Kondor.

Encontrei este link que ajudou bastante:

http://aplawrence.com/SCOFAQ/FAQ_scotec7printtofile.html

Então os passos que fiz e cheguei ao sucesso são...

Criar a pasta que irá conter o script que receberá a os dados de impressão:

# mkdir /usr/local/PDFprinter


Criar uma outra pasta que irá conter o arquivos de PDF:

# mkdir /var/PDFprinter


Gerar este arquivo de script em /usr/local/PDFprinter/start que será usado para preparar o device da impressora e manter o device em sincronização com o script de impressão:

mknod /dev/PDFprinter p
chmod 777 /dev/PDFprinter
while true
do
cat /dev/PDFprinter | /usr/local/PDFprinter/print
done


Permissões de execussão:

# chmod +x /usr/local/PDFprinter/start


Gerar o script de impressão em /usr/local/PDFprinter/print:

date=`date "+%G%m%d_%H%M%S"`
tempFileName="/tmp/PDFprinter_$date"
while read stuff
do
echo $stuff >> $tempFileName
done
cat $tempFileName | grep -v "#####" > $tempFileName.tmp
mv $tempFileName.tmp $tempFileName
fileName="/var/PDFprinter/PDFprinter_$date.pdf"
ps2pdf -sPAPERSIZE=a4 $tempFileName $fileName


O comando ps2pdf faz parte do pacote ghostscript:

http://www.sunfreeware.com/programlistsparc10.html#ghostscript

E para definir o tamanho do papel é com o parâmetro ps2pdf -sPAPERSIZE=a4 ...

Permissões de execussão:

# chmod +x /usr/local/PDFprinter/print


Preparar e iniciar o device da impressora:

# /usr/local/PDFprinter/start &


Registrar o device da impressora e activar a impressora:

# lpadmin -p PDFPrinter -v /dev/PDFprinter
# accept PDFPrinter
# enable PDFPrinter


Agora é só mandar alguma coisa em PostScript para imprimir nesta impressora e ver o resultado em arquivos temporários em /tmp, e caso gere o PDF com sucesso deverá estar em /var/PDFprinter;

Monday, September 28, 2009

Process ID

Quando se faz um serviços Unix em Java para poder fazer os scripts de stop, kill, etc, é preciso o PID (número do processo), então para tal é preciso gerar um arquivo que contém esta informação.

Portanto este código faz exatamente isto, gera um arquivo com o PID da aplicação Java:

java.io.BufferedWriter bw = null;
try {
bw = new java.io.BufferedWriter(new java.io.OutputStreamWriter(new java.io.FileOutputStream("my.pid")));
String pidInfo = java.lang.management.ManagementFactory.getRuntimeMXBean().getName();
bw.write(pidInfo.substring(0, pidInfo.indexOf('@')));
} finally {
if (bw != null) bw.close();
}

Friday, June 19, 2009

Jetty e Web Services com JAX-WS

O Jetty é um poderoso servidor web, disto não há dúvidas, mas no que toca a web services paira uma nuvem negra, não há muita documentação e nem integração com IDEs.

Depois de pesquisar um pouco vi que era possível integrar com o JAX-WS.

Não encontrei nenhuma explicação detalhada de como fazer, e que explicasse também a parte do cliente, então resolvi fazer o download do JAX-WS e ver os exemplos e a documentação, e a conclusão que cheguei segue abaixo.

Levando em consideração que já tem o Jetty configurado e rodando, no meu caso foi a versão 6 e com o JDK 6.

Fazer o download do JAX-WS no meu caso foi a versão 2.1.7 em:

https://jax-ws.dev.java.net/

Descompactar e em jaxws-ri/docs/UsersGuide.html#3.1_Server da para ver alguma explicação de como fazer.

Copiar todos os jars que estão na pasta lib do JAX-WS para a pasta lib/ext do Jetty.

Agora no seu projeto web criar as seguintes classes de exemplo (também estão em jaxws-ri/samples/fromjava/src), aqui dei alguma melhoria nas anotações:

-> fromjava/server/AddNumbersImpl.java:

package fromjava.server;
import javax.jws.WebService;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.ws.WebServiceContext;
import javax.xml.ws.handler.MessageContext;
import javax.annotation.Resource;
@WebService
public class AddNumbersImpl {
@Resource
private WebServiceContext context;
@WebMethod(operationName = "add")
public int addNumbers(@WebParam(name = "first") int number1, @WebParam(name = "second") int number2) throws AddNumbersException {
// SERVLET - IF NEED SERVLET RESOURCES. For example to do session control.
MessageContext messageContext = context.getMessageContext();
ServletContext servletContext = (ServletContext)messageContext.get(MessageContext.SERVLET_CONTEXT);
HttpServletRequest servletRequest = (HttpServletRequest)messageContext.get(MessageContext.SERVLET_REQUEST);
HttpServletResponse servletResponse = (HttpServletResponse)messageContext.get(MessageContext.SERVLET_RESPONSE);
// --
if (number1 < 0 || number2 < 0) {
throw new AddNumbersException("Negative number cant be added!",
"Numbers: " + number1 + ", " + number2);
}
return number1 + number2;
}
}

-> fromjava/server/AddNumbersException.java:

package fromjava.server;
public class AddNumbersException extends Exception {
String detail;
public AddNumbersException (String message, String detail) {
super (message);
this.detail = detail;
}
public String getDetail () {
return detail;
}
}

Acrescentar no web.xml:

<listener>
<listener-class>com.sun.xml.ws.transport.http.servlet.WSServletContextListener</listener-class>
</listener>
<servlet>
<description>JAX-WS endpoint - fromjava</description>
<display-name>fromjava</display-name>
<servlet-name>fromjava</servlet-name>
<servlet-class>com.sun.xml.ws.transport.http.servlet.WSServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>fromjava</servlet-name>
<url-pattern>/addnumbers</url-pattern>
</servlet-mapping>

E junto com o web.xml criar o arquivo sun-jaxws.xml:

<?xml version="1.0" encoding="UTF-8"?>
<endpoints xmlns='http://java.sun.com/xml/ns/jax-ws/ri/runtime' version='2.0'>
<endpoint
name='fromjava'
implementation='fromjava.server.AddNumbersImpl'
url-pattern='/addnumbers'/>
</endpoints>

Feito isto, é rodar o Jetty e ver o WSDL:

http://localhost:8080/WSTester/addnumbers?WSDL

Onde o WSTester é o nome do seu projeto.

Agora temos que criar as classes para chamar o web service, para isto o JAX-WS trás o bin/wsimport, no meu caso como estou no Linux vou usar o .sh, caso esteja no Windows é só usar o .bat, criar as pastas src e dest dentro da pasta do JAX-WS.

$ chmod +x bin/wsimport.sh
$ mkdir dest
$ mkdir src
$ bin/wsimport.sh -d dest -s src http://localhost:8080/WSTester/addnumbers?WSDL

O wsimport vai gerar o código das classes para invocar o web service na pasta src, e as classes compiladas em dest.

Eu peguei na pasta src/fromjava e copiei para o um outro projeto de aplicação do tipo consola em Java. Assim pode chamar o web service:

TestAddNumbers.java:

package javaapplicationWSTester;
import fromjava.server.*;
public class TestAddNumbers {
public static void main(String[] args) throws AddNumbersException_Exception {
int result = new AddNumbersImplService().getAddNumbersImplPort().add(3, 5);
System.out.println(result);
}
}

O processo é simples, ter a classe do web service, configurar no web.xml e no sun-jaxws.xml, gerar as classes de invocação com o wsimport e boa :P

Monday, May 18, 2009

SUDO + PHP - Executando comandos como Root no PHP

É preciso saber qual é o usuário de execução do PHP:

<?php echo exec("whoami"); ?>


Editar o /etc/sudoers, este arquivo tem que ter permissões apenas de leitura, não pode ficar com permissões de escrita, por isso antes de editar o arquivo dê permissões de escrita:

# chmod u+w /etc/sudoers


Depois de editar, tire a permissão de escrita:

# chmod u-w /etc/sudoers


Então adicionar no /etc/sudoers no fim do arquivo:

%USUARIO_DO_PHP ALL= NOPASSWD: /CAMINHO/DO/COMANDO


Se não colocar no fim do arquivo pode ter outra regra depois que anule esta nova regra.

No PHP é só executar o comando com o SUDO:

<?php echo exec("sudo /CAMINHO/DO/COMANDO.sh"); ?>


Caso não tenha sucesso, verifique os logs de segurança:

# tail -n 100 /var/log/secure


Se tiver esta mensagem de erro:

May 18 16:56:41 SERVIDOR sudo: USUARIO_DO_PHP : sorry, you must have a tty to run sudo ; TTY=unknown ; PWD=/PASTA/DO/PHP ; USER=root ; COMMAND=/CAMINHO/DO/COMANDO


Então é preciso ir no /etc/sudoers e comentar a seguinte linha que contém o "Defaults requiretty", assim:

# Defaults requiretty


Assim deverá funcionar.

Friday, March 13, 2009

Ubuntu, iniciando o Compiz & NVIDIA com Antialiasing

Para por configurar o Compiz no arranque do Gnome, vá em:

System -> Preferências -> Sessões


E adicione o Compiz:

Nome: Compiz
Comando: compiz --replace &
Comentário: Desktop 3D


Fica assim o Compiz configurado para sempre iniciar no arranque do Gnome.

Depois de configurar a qualidade de antialiasing e outras configurações em:

Systema -> Administração -> NVIDIA X Server Settings

Fazer as devidas configurações, principalmente a do "Antialiasing Settings".

Para que o Gnome carregue as configurações do driver no arranque da sessão, edite o arquivo:

/usr/share/xsessions/gnome.desktop


A linha do parâmetro "Exec", para:

Exec="nvidia-settings -l && /usr/bin/gnome-session"


Assim sempre que o Gnome iniciar ele vai aplicar as configurações do driver e rodar o Compiz.