Palavras-Chave: Linux
Pessoal, primeiramente peço perdão a Alah por não ter postado neste blog por muito tempo... Continuarei neste rítmo uma vez que a correria tá braba!!
Eu tava me preparando pra escrever isso há dias mas não sobrava tempo. O que ocorre é que existe um recurso nem tão freqüentemente utilizado em ambientes UNIX, chamado de umask. O umask nada mais é do que uma "mascara" utilizada para definir as permissões padrões de novos arquivos e diretórios. Mais ao fim eu explico o "espírito" da umask, mas por enquanto vamos contar um pouco da aventura.
No tempo que eu era um "gurizote", nem tão barrigudo quanto sou hoje, eu aprendi, com repetidores de lendas, que a tal da umask era um "número", que subtraído de 777 nos dava a permissão que ia ser utilizada para novos diretórios, e que subtraído de 666 nos dava a permissão que ia ser utilizada para novos arquivos.
Quando eu comprei alguns livros para estudo, como o "Certificação Linux", do Uirá Ribeiro(recomendo, propaganda grátis hehehe), que me confirmavam o que eu havia lido(e nunca havia testado tão a fundo). Nos exemplos que sempre são passados por estes repetidores de informação(me incluo neles) temos sempre a típica umask que coincide com o cálculo errôneo, e que é a umask padrão de todos os sistemas: a umask 022.
Na minha vida prática, só precisei utilizar outra umask, DURANTE MUITOS ANOS, quando trabalhei com SVN sobre SSH. O que acontecia neste caso é que os novos arquivos do repositório SVN precisavam ter permissão de escrita para o grupo também, senão nenhum outro usuário alteraria o conteúdo criado por outro no repositório. Para isso ser possível, alterei de 022 para 002 a umask do sistema. Com esta umask, também havia a dita coincidência com o "lendário cálculo"(os números '0' e '2' são 'mágicos' em certas operações matemáticas binárias).
Esses dias estava com uma turma de alunos, aonde, como muito ocorre com todo mundo, fui um repetidor de informações no que se refere à umask. Normalmente eu não falava dela em aulas, pois para mim aquilo parecia inútil até o recente episódio do SVN+SSH. Resolvi passar ela para o pessoal, que foi fazer uns testes e achou alguns casos que não batiam. Desde então, fui pesquisar e achei 50% das informações na internet falando da "lenda da subtração de umask". as outras 50% eram outras informações semelhantes que não batiam também. As man pages umask(2), open(2), creat(2) e mkdir(2) também não foram muito úteis na tarefa, pois não pareciam estar escritas de maneira clara.
Então, lá fui eu ler os fontes do kernel. No diretório 'fs' dos fontes nós encontramos a informação. Lendo os arquivos Procurando pelas funções das system calls(sys_mkdir, sys_open e sys_creat), achei o arquivo namei.c, aonde haviam 2 repetições de um trexo bem interessante do código:
if (!IS_POSIXACL(nd.dentry->d_inode))
mode &= ~current->fs->umask;
Então percebi que as man pages, apesar de pouco claras, estavam 100% certas. O cálculo da permissão se dá por uma operação AND(binária, bit a bit) entre o modo passado. Claro que para tudo há um porém.
Concluí em alguns testes que a umask só é aplicada quando os valores default são passados para as system calls que utilizam a umask(as system calls creat, open, mknod e mkdir, que criam nodos no sistema de arquivos, e os valores default são 777 para diretório e 666 para arquivo). Fica a promessa para mais adiante eu investigar aonde essa condição de verificação da umask é aplicada e mandar para todos a informação completa.
Como falei lá em cima, entendi o "espírito" da umask, que é basicamente você definir os bits que você quer que NÃO sejam ativados para arquivos e diretórios novos. Simples assim.
Para quem não é acostumado com matemática binária, segue a referência para entender as operações envolvidas na umask(AND e NOT):
Lembro a todos ainda que isso tudo depende muito de como os programas fazem a chamada, uma vez que se o programa requisitado passar na system call o modo diferente do padrão, a umask não será aplicada. Um caso aonde isso ocorre é quando passamos ao mkdir a flag '-m' indicando um modo diferente.
Fico devendo ainda uma explicação melhor sobre essa excessão, pois ainda não localizei aonde esse tratamento é feito. Estou analisando a libc também para encontrar o "caminho completo". Se alguém tiver uma sugestão ou resposta antes, comenta aí!!
OBS: Saindo em breve do forno um artigo sobre uso de NAT no user-space através das libs do netfilter. Esperem e verão!
Classificação do conteúdo: SÉRIO
Sobre mim:
Curriculum Vitae
Site Pessoal
quinta-feira, 4 de junho de 2009
umask, mistérios e lendas
às
Quinta-feira, Junho 04, 2009
1 ociosos comentaram
Links para esta postagem
O Inútil que escreveu foi
"Brunildz" Bruno Moreira Guedes
segunda-feira, 10 de novembro de 2008
PuTTY: Utilizando Chaves RSA e DSA no SSH
Palavras-Chave: SSH, PuTTY
Primeiramente peço desculpa aos inúmeros leitores(cerca de 2 ou 3 incluindo eu mesmo) deste blog por ficar alguns dias sem postar. As coisas andam um tanto corridas ultimamente.
Mas deixando o blá-blá-blá sobre o blog, agora vamos ao blá-blá-blá sobre o que vou escrever. Mas antes, para os apressados, informo que podem procurar pelo subtítulo "Finalmente..." logo abaixo para "meterem a mão na massa" logo. Muita gente, mas muita gente mesmo, durante minha vida profissional me fez essa bendita pergunta: eu uso Windows. Como posso acessar um servidor Linux via SSH estando no Windows??
Para esta pergunta, de início eu recomendava a utilização do OpenSSH para Windows(minha predileta, apesar de eu não utilizar o sistema do tiu Bill quase nunca). Os problemas que tiram a aceitação de muita gente à minha recomendação normalmente são algo como resolução ruim do prompt de comando do Windows(a Microsoft podia caprichar mais no cmd, não podia?), problemas com configuração de tipo de terminal, usuários que não querem iniciar o programa em linha de comando, entre muitos e muitos outros.
Tive mais sucesso quando passei a recomendar a eles o uso do PuTTY, que é mais "win-user friendly", gera menos transtornos para configuração de tipo de terminal/mapa de teclas, etc. Nosso amigo PuTTY só tem um pequeno problema: Incompatibilidade com chaves SSH2-RSA e SSH2-DSA geradas pelo utilitário ssh-keygen, do OpenSSH.
Estive fazendo algumas revisões de políticas de segurança em um cliente. Durante o processo, foi definida a adoção de chaves RSA para acesso via SSH. O problema logo apareceu: algumas pessoas acessam o servidor via PuTTY.
Sendo assim, vi que este velho problema precisava ser eliminado. Googlei por algum tempo e achei este link aqui(em inglês), que explica como proceder nesta situação. Abaixo estarei explicando em Português o procedimento e a idéia.
Primeiramente, precisaremos de um software que pode ser obtido no site do PuTTY: o puttygen.exe. Este é o software que trabalha com as chaves em um formato próprio do PuTTY, diferente do formato SSH2.
Abaixo, selecione(em Parameters) a opção "SSH-2 RSA" ou "SSH-2 DSA", dependendo do algoritmo desejado. Não recomendo a opção "SSH-1", pois ela é para o protocolo versão 1 do SSH, que hoje está praticamente em desuso.
No campo de texto com rótuno "Number of bits in a generated key", você pode manter o valor 1024, ou aumentar(normalmente 1024bits é mais que o suficiente) para 2048 por exemplo(se o servidor contiver, por exemplo, fotos de uma bebedeira sua, tornando muito válido perder um pouco o desempenho para incrementar a este ponto a segurança).
Agora que está tudo parametrizado, clique em Generate. Segundo a mensagem da interface, a randomização da chave é de acordo com o movimento do mouse(que coisa não?)... Sendo assim, procure executar movimentos bem estranhos com o mouse nesta hora(é o que o software diz, não li os fontes para conferir).
Pronto... Após uma espera de cerca de 30 segundos, sua chave está "na mão", bastam algumas informações de como será salva a chave. Você pode definir um comentário(que sempre será visto no momento que o PuTTY envia a chave). E, você pode(e deve) definir um passphrase para a chave.
No campo "Key Passphrase", xingue sua sogra. No campo "Passphrase Confirm", xingue-a novamente(da mesma maneira). Ali valem espaços e caracteres especiais, então pode xingar a vontade.
Pronto, sua chave está gerada. Abaixo da barra de menus aparece um campo somente leitura, com a chave pública pronta para você adicionar no seu ~/.ssh/authorized_keys (ou outro arquivo de chaves autorizadas que esteja definido no /etc/ssh/sshd_config).
Você PODE salvar sua chave pública clicando no botão "Salve public key", e DEVE salvar sua chave privada através do botão "Save private key".
$ ssh-keygen -f nome-do-arquivo -t rsa -b 1024
No exemplo foi usado RSA, de 1024bits, e o arquivo "nome-do-arquivo", mas estes parâmetros podem ser trocados. O comando de exemplo gera um arquivo chamado "nome-do-arquivo", contendo nossa chave privada, e outro chamado "nome-do-arquivo.pub", contendo a chave pública. A Passphrase será perguntada em um prompt.
Agora vamos importá-la para uso no PuTTY. Abra o PuTTYGen novamente, e clique no botão "Load". Selecione o arquivo, digite a senha(se houver). Se necessário, redefina o comentário e/ou o passphrase.
E, finalmente, salve sua chave. Agora na hora de usar o PuTTY, quando aparecer a tela de configurações dele, expanda na esquerda o item "Connection", e logo após o subitem "SSH". Clique no subitem "Auth".
Aparecerá um campo com o label "Private key file for authentication", com um botão "Browse..." ao lado. Clique no botão, procure a chave, e ela estará pronta para ser usada.
Espero que o artigo tenha sido útil!!
[]'s
Classificação do conteúdo: SÉRIO
Sobre mim:
Curriculum Vitae
Site Pessoal
Primeiramente peço desculpa aos inúmeros leitores(cerca de 2 ou 3 incluindo eu mesmo) deste blog por ficar alguns dias sem postar. As coisas andam um tanto corridas ultimamente.
Mas deixando o blá-blá-blá sobre o blog, agora vamos ao blá-blá-blá sobre o que vou escrever. Mas antes, para os apressados, informo que podem procurar pelo subtítulo "Finalmente..." logo abaixo para "meterem a mão na massa" logo. Muita gente, mas muita gente mesmo, durante minha vida profissional me fez essa bendita pergunta: eu uso Windows. Como posso acessar um servidor Linux via SSH estando no Windows??
Para esta pergunta, de início eu recomendava a utilização do OpenSSH para Windows(minha predileta, apesar de eu não utilizar o sistema do tiu Bill quase nunca). Os problemas que tiram a aceitação de muita gente à minha recomendação normalmente são algo como resolução ruim do prompt de comando do Windows(a Microsoft podia caprichar mais no cmd, não podia?), problemas com configuração de tipo de terminal, usuários que não querem iniciar o programa em linha de comando, entre muitos e muitos outros.
Tive mais sucesso quando passei a recomendar a eles o uso do PuTTY, que é mais "win-user friendly", gera menos transtornos para configuração de tipo de terminal/mapa de teclas, etc. Nosso amigo PuTTY só tem um pequeno problema: Incompatibilidade com chaves SSH2-RSA e SSH2-DSA geradas pelo utilitário ssh-keygen, do OpenSSH.
Estive fazendo algumas revisões de políticas de segurança em um cliente. Durante o processo, foi definida a adoção de chaves RSA para acesso via SSH. O problema logo apareceu: algumas pessoas acessam o servidor via PuTTY.
Sendo assim, vi que este velho problema precisava ser eliminado. Googlei por algum tempo e achei este link aqui(em inglês), que explica como proceder nesta situação. Abaixo estarei explicando em Português o procedimento e a idéia.
Finalmente...
Agora que já expliquei o contexto aonde utilizei, vamos para a parte que todos gostam. Eu prezumo que todos tenham conhecimento de como configurar o SSH no servidor e adicionar a chave na lista das chaves autorizadas para os usuários, então não vou entrar neste detalhe(fica para outro artigo).Primeiramente, precisaremos de um software que pode ser obtido no site do PuTTY: o puttygen.exe. Este é o software que trabalha com as chaves em um formato próprio do PuTTY, diferente do formato SSH2.
O Primeiro Caminho
O primeiro modo para fazermos o PuTTY utilizar as chaves, é o mais simples: gerar a chave no próprio PuTTY. Para isso, abra o puttygen(faça o download apartir do link que eu coloquei acima).Abaixo, selecione(em Parameters) a opção "SSH-2 RSA" ou "SSH-2 DSA", dependendo do algoritmo desejado. Não recomendo a opção "SSH-1", pois ela é para o protocolo versão 1 do SSH, que hoje está praticamente em desuso.
No campo de texto com rótuno "Number of bits in a generated key", você pode manter o valor 1024, ou aumentar(normalmente 1024bits é mais que o suficiente) para 2048 por exemplo(se o servidor contiver, por exemplo, fotos de uma bebedeira sua, tornando muito válido perder um pouco o desempenho para incrementar a este ponto a segurança).
Agora que está tudo parametrizado, clique em Generate. Segundo a mensagem da interface, a randomização da chave é de acordo com o movimento do mouse(que coisa não?)... Sendo assim, procure executar movimentos bem estranhos com o mouse nesta hora(é o que o software diz, não li os fontes para conferir).
Pronto... Após uma espera de cerca de 30 segundos, sua chave está "na mão", bastam algumas informações de como será salva a chave. Você pode definir um comentário(que sempre será visto no momento que o PuTTY envia a chave). E, você pode(e deve) definir um passphrase para a chave.
No campo "Key Passphrase", xingue sua sogra. No campo "Passphrase Confirm", xingue-a novamente(da mesma maneira). Ali valem espaços e caracteres especiais, então pode xingar a vontade.
Pronto, sua chave está gerada. Abaixo da barra de menus aparece um campo somente leitura, com a chave pública pronta para você adicionar no seu ~/.ssh/authorized_keys (ou outro arquivo de chaves autorizadas que esteja definido no /etc/ssh/sshd_config).
Você PODE salvar sua chave pública clicando no botão "Salve public key", e DEVE salvar sua chave privada através do botão "Save private key".
O Segundo Caminho
Este é para aqueles que já tem uma chave gerada no OpenSSH, e precisam utilizá-la no PuTTY. Somente para ilustrar, o procedimento básico para gerar uma chave no OpenSSH é a execução do comando:$ ssh-keygen -f nome-do-arquivo -t rsa -b 1024
No exemplo foi usado RSA, de 1024bits, e o arquivo "nome-do-arquivo", mas estes parâmetros podem ser trocados. O comando de exemplo gera um arquivo chamado "nome-do-arquivo", contendo nossa chave privada, e outro chamado "nome-do-arquivo.pub", contendo a chave pública. A Passphrase será perguntada em um prompt.
Agora vamos importá-la para uso no PuTTY. Abra o PuTTYGen novamente, e clique no botão "Load". Selecione o arquivo, digite a senha(se houver). Se necessário, redefina o comentário e/ou o passphrase.
E, finalmente, salve sua chave. Agora na hora de usar o PuTTY, quando aparecer a tela de configurações dele, expanda na esquerda o item "Connection", e logo após o subitem "SSH". Clique no subitem "Auth".
Aparecerá um campo com o label "Private key file for authentication", com um botão "Browse..." ao lado. Clique no botão, procure a chave, e ela estará pronta para ser usada.
Espero que o artigo tenha sido útil!!
[]'s
Classificação do conteúdo: SÉRIO
Sobre mim:
Curriculum Vitae
Site Pessoal
às
Segunda-feira, Novembro 10, 2008
1 ociosos comentaram
Links para esta postagem
O Inútil que escreveu foi
"Brunildz" Bruno Moreira Guedes
Marcadores:
Linux
quarta-feira, 5 de novembro de 2008
OBhrama vence eleições, mas AAntartica ainda é a preferida
O candidado à presidência da república dos Estados Unidos da Almôndega, Barata OBhrama, se elege Presidente. No seu discurso após eleito, ele diz que irá mudar a américa.
O presidente tem diversos poderes capazes de mover as placas tectônicas, planejando utilizar estratégias de guerra melhores que as do antigo presidente, Jorge War Bucha. Ele diz que o primeiro lugar a ser invadido será a Russia, devido à proximidade e a tendência geografica. Devido a isso, anunciou que a américa irá mudar-se para acima da Azia, o que foi chamado de "movimento sal de frutas" pelos defensores de OBhrama.
Informou Bruno.
[]'s
Classificação do conteúdo:DESCONFIAVEL
Sobre mim:
Curriculum Vitae
Site Pessoal
O presidente tem diversos poderes capazes de mover as placas tectônicas, planejando utilizar estratégias de guerra melhores que as do antigo presidente, Jorge War Bucha. Ele diz que o primeiro lugar a ser invadido será a Russia, devido à proximidade e a tendência geografica. Devido a isso, anunciou que a américa irá mudar-se para acima da Azia, o que foi chamado de "movimento sal de frutas" pelos defensores de OBhrama.
Informou Bruno.
[]'s
Classificação do conteúdo:
Sobre mim:
Curriculum Vitae
Site Pessoal
às
Quarta-feira, Novembro 05, 2008
0
ociosos comentaram
Links para esta postagem
O Inútil que escreveu foi
"Brunildz" Bruno Moreira Guedes
Marcadores:
Inutilidades
terça-feira, 28 de outubro de 2008
Desenvolvimento de Módulos Para o Kernel do Linux II
Palavras-chave: Linux, C, Kernel
Após o "incrível sucesso" do primeiro artigo, decidi transformá-lo em uma "série" de artigos, e este será o nosso segundo. Primeiramente gostaria de agradecer ao pessoal que me incentivou. Eu imaginava ter uns 100 acessos, mas o primeiro artigo teve aproximadamente 900 visualizações, fora o pessoal que comentou me incentivando.
Só prestando um esclarecimento, eu nem imaginava que este tema seria de tanto agrado do publico. Imaginei que isso fosse alvo de interesse apenas dos meus amigos mais nerds :-) Mas vou aproveitar que me interesso bastante pelo tema e vou procurar escrever com freqüência(ou nem tanta assim) sobre o tema, tudo dependerá de como andam as coisas para mim(fim da semana passada e esta agora estão corridinhas!!).
Agora chega de lero-lero e vamos ao que interessa: eu deixei muita coisa "em aberto" no primeiro artigo. Uma pessoa deixou(anonimamente) o comentário dizendo que faltava comentar o código. Bom, quando eu enviei aquele post eu estava com um pouco de pressa(como agora hehehe), mas agora vamos explicá-lo melhor!!
Primeiramente, um pouco do "geral" sobre as bibliotecas do kernel. Obviamente, um sistema operacional requer "rotinas diversas" para uso interno do próprio kernel. São rotinas básicas que usamos em diversos diferentes locais. Um destes exemplos é o printk(), que é um "analogo" ao printf() da libc.
Antes de mais nada, alguns podem ter a dúvida: e porque não utilizar as funções da libc mesmo, que são padronizadas com a linguagem, mais completas e de utilização mais fácil?? Bom, a resposta é simples: o kernel não faz a mínima idéia de que exista uma libc. Quando você está no kernel space só existe o kernel. Bibliotecas, para o ver do kernel, são meros arquivos em formato ELF, referenciados por outros meros arquivos também em formato ELF. A única coisa que o kernel sabe é executar system calls[1] nestes arquivos.
Estas bibliotecas são documentadas naquela API do kernel, cujo link coloquei no outro artigo. Retomando o foco do assunto, no post anterior eu não havia escrito muitos detalhes do código. Revirando meus links, encontrei o link do 'The Linux Kernel Module Programming Guide', que eu não me recordava, mas havia sido minha inspiração na escrita daquele código de exemplo. Todos os detalhes que menciono aqui são semelhantes aos do guia, que é uma das fontes deste artigo, juntamente com os fontes do kernel.
Primeiramente, na estrutura do código, temos nossos dois arquivos: errcon.h e errcon.c. Prezumo que todos que estejam lendo este arquivo conheçam razoavelmente os padrões que comumente são adotados para a divisão de códigos entre arquivos de cabeçalho(.h, com declarações) e arquivos de implementação(os .c ou .cpp no caso de C++). Caso não conheçam, recomendo "googlar" sobre o assunto
Em nosso header, primeiro incluimos alguns headers com declarações de símbolos que utilizaremos. Logo após, definimos algumas "macros úteis". São apenas para concentrar o valor de algumas coisas ali ao começo. Nesta época eu não era tão "avesso" ao uso de macros.
Depois temos nossaas variáveis globais e funções. Quero que atentem bastante às funções 'ec_dev*' e à estrutura 'opers', do tipo file_operations. Elas são as responsáveis pelo funcionamento da parte do arquivo de dispositivo(a entrada no /dev) que nosso módulo nos disponibiliza.
Como vocês podem notar, registramos em uma estrutura file_operations todas as operações relacionadas a arquivo que nosso módulo realiza. Mas neste módulo não utilizamos todas as possibilidades. Mesmo assim, quero que vocês conheçam a declaração da estrutura para verificar as operações disponíveis para manipulação:
Agora, reparem também, ainda no noso arquivo de cabeçalho, que temos um ponteiro para uma declaração do tipo proc_dir_entry. Ela é uma "análoga", se é que podemos dizer isso, da estrutura file_operations. Mais adiante compreenderemos porque ela é declarada na forma de um ponteiro aqui em cima. Por enquanto, adianto apenas que as funções ec_procread e ec_procwrite serão associadas a ela, juntamente com algumas outras informações. Veja declaração da estrutura:
Agora vamos finalmente ao nosso arquivo de implementação(errcon.c). A primeira coisa que vemos(após a inclusão do cabeçalho errcon.h) é implementação da função ec_devwrite. O que ela faz, basicamente, é implementar a operação write no dispositivo. Simplesmente lê o buffer de leitura(localizado no user-space, por isso lido com get_user e não diretamente) e o salva em nossa lista dos ultimos 4 erros.
Quanto ao local para onde os ponteiros de nosso array de ponteiro apontam, entenderemos mais tarde. De momento saiba apenas que é para um buffer de dados. Repare que como este módulo é apenas um teste, NENHUM TIPO DE PREVENÇÃO CONTRA BUFFER OVERFLOW FOI TOMADO. Para ocorrer um buffer overflow, basta escrevermos um número de bytes maior do que o tamanho do buffer. E, para este buffer overflow ser mais grave, basta que isso ocorra no ultimo dos 4 buffers, ou então que seja grande o suficiente para passar do fim do ultimo buffer no caso dos outros 3 buffers. Isso tudo se deve a grande preguiça que o autor tem de implementar um contador de caracteres(int i=buf_limit e buf_limit-- dentro do loop :-) hehehe).
Agora prosseguindo, temos nossa função ec_procread, que responde à operação read do arquivo de dispositivo. Ela basicamente escreve os dados do buffer do módulo(com a mensagem do ultimo erro recebido pelo módulo) no buffer do user-space(lá aonde o processo está fazendo a leitura. A "idéia" do put_user é a mesma do get_user, com diferença apenas operacional(um coloca no usuário e o outro pega do usuário, :-) entenda este comentário como quiser).
Agora vamos para a ec_open. Ela é responsável pela abertura do arquivo (citação de Dr. Obvio). Para evitar multiplos processos efetuando I/O no módulo, fazemos um "controle de abertura". No mais, a função também reseta o buffer de leitura. Como vocês viram, na ec_read nós apenas liamos apartir do buffer. Mas aí ficava no ar a pergunta: daonde vinha a posição correta do buffer?
É simples. Imagine que o processo não leia todos os dados de uma vez só. Suponhamos que nós tenhamos uma string de erro "hahehihohu". O tamanho de buffer utilizado pelo processo que lê o dispositivo é 2. Acompanhe a seqüência:
Só para constar, vejamos a linha aonde fazemos a chamada:
bruno@bw1:~$ lsmod
Module Size Used by
parport_pc 27812 0
parport 34760 1 parport_pc
yenta_socket 27148 1
Repare no terceiro campo(Used By), aonde vemos um número, algumas vezes seguido de uma lista de módulos. Este número é o contador de usos. Os módulos listados são outros módulos que utilizam recurso do listado. Isso significa que o try_module_get não serve apenas para indicar quantas vezes o arquivo de dispositivo foi aberto, mas para n outras coisas.
A função ec_devclose dispensa comentários. Apenas esclareço que o try_module_put decrementa o contador de uso do módulo. Na função ec_procread, simplesmente escrevemos no buffer uma mensagem formatada, dizendo o número de erros e o ultimo erro que foram registrados. Na ec_procwrite, utilizamos a função copy_from_user para ler 1 caractere do buffer recebido. Caso este caractere lido seja 'c', é efetuado um loop percorrendo o buffer de mensagens de erro e apagando seus dados.
Bom, agora que todos já entenderam "cada pedaço" da coisa, vamos ao "ponto de junção", aonde a gente "junta toda essa cosia e faz virar uma só". É o nosso "main 1", o init_module. Como eu já havia citado anteriormente, este "cara" é o responsável por disponibilizar aquilo que o módulo fornece, e inicializar o "serviço" do módulo.
A primeira coisa que ele faz é preparar os nossos buffers. Como você pode ver no código, de acordo com minha explicação sobre ponteiros, criamos um array global, de 16386 bytes(de acordo com a macro EC_PROCFILE_BUFF_SIZE) apontado por ec_errors, ainda no nosso header(errcon.h). Agora nós dividimos a área apontada por ec_errors em 4 partes, cada uma com seu início apontado por um dos ponteiros do array de ponteiros apontado por ec_error(esta frase é meio confusa, mas tente reler umas 2 vezes, ou se tiver raciocínio visual e precisar, não tenha vergonha de desenhar num papel ao longo da leitura para entender a frase - e se ainda não entender, recomendo ler novamente a explicação sobre ponteiros). Observe que temos 2 poréms nesta história: o primeiro é que ao longo do programa não utilizamos nenhuma mensagem além da ultima. Isso foi uma falha minha, pois na época eu não tinha noção alguma do que significava "requisito de software".
O segundo porém, é um pequeno errinho meu. Se vocês acompanharem o loop aonde é feita essa divisão da área, vocês vão ver que todos os 4 ponteiros apontam para a mesma área, que seria "a segunda posição" das 4 na qual dividimos. Apartir de agora, quando vocês olharem o código ele estará atualizado, e o valor:
Após dividirmos o nosso array em 4 buffers "distintos", criamos nossa entrada no sistema de arquivos proc. Fazemos um tratamento de erro, e caso ocorra realmente algo errado, nós efetuamos a tentativa de remoção da entrada recém criada(ou não criada, não temos como ter certeza, removemos para evitar uma entrada desagradável e permanente no /proc).
Logo depois, criamos nossa entrada no /dev. Só para constar, caso o retorno do register_chrdev seja negativo(erro) em vez do major, temos certeza de que o device não foi criado(diferentemente da entrada no /proc). E, então, removemos a entrada.
Em ambos os tratamentos de erro, retornamos negativo. Ou seja: indicamos pro kernel que ocorreram erros e ele deve cancelar o carregamento do módulo. O kernel, por sua vez, informa o modutils disso, e este por sua vez joga uma mensagem de erro para o usuário. É vital fazermos isso para evitar problemas!
Observem a diferença entre as abordagens da criação do arquivo de dispositivo e da criação da entrada no /proc. No primeiro caso, criamos a estrutura com as informações das operações de arquivo, e depois chamamos a função responsável "registrando" esta estrutura. No segundo, criamos apenas um ponteiro e utilizamos uma função que retorna a área da estrutura. Nesta função, passamos apenas o nome do arquivo a ser criado.
Uma vez registradas, estas estruturas podem ser modificadas a qualquer momento. É o que fazemos com a estrutura do nosso arquivo no /proc. Ao remover, passamos como parâmetro o endereço de proc_root. A entrada proc_root nada mais é do que outra estrutura proc_dir_entry, que representa a raiz do /proc.
Veja que depois nós modificamos nossa entrada no /proc acessando diretamente a estrutura. Logo após isso, o restante do código é apenas complementar: imprimimos com printk as mensagens informando alguns detalhes do módulo.
Retornamos 0(o valor de SUCCESS) para indicar sucesso na operação. Valores negativos indicam erro e cancelam o carregamento do módulo.
Quase no fim, temos nosso cleanup_module. Basicamente, ele desregistra nosso dispositivo de caractere, e remove nossa entrada do /proc.
Ao fim do código, utilizamos algumas macros para registrar algumas informações sobre o módulo: sua licensa, seu autor, o nome de seu dispositivo, e sua descrição. Você pode visualizar suas informações com o comando:
$ modinfo errcon.ko
Finalmente, peço desculpas pela demora a postar algo novo. É que esta semana está corrida!! Espero que gostem, em breve mais conteúdo sobre o kernel!!
OBS: se alguém quiser que eu escreva sobre algum tema específico, e estiver ao meu alcance, é só dizer que se tiver no meu domínio e se encaixar no meu "time" eu escrevo.
Agradeço a atenção de todos os meus leitores. Caso não saibam, tem um milhão lendo agora esse blog. E em seguida esse milhão vai ser cozido :P
[1] System Calls, ou chamadas de sistema, são "funções" disponibilizadas pelo sistema operacional aos aplicativos. Geralmente bem básicas. Vide man page for syscalls(2)
Classificação do conteúdo: SÉRIO
Sobre mim:
Curriculum Vitae
Site Pessoal
Após o "incrível sucesso" do primeiro artigo, decidi transformá-lo em uma "série" de artigos, e este será o nosso segundo. Primeiramente gostaria de agradecer ao pessoal que me incentivou. Eu imaginava ter uns 100 acessos, mas o primeiro artigo teve aproximadamente 900 visualizações, fora o pessoal que comentou me incentivando.
Só prestando um esclarecimento, eu nem imaginava que este tema seria de tanto agrado do publico. Imaginei que isso fosse alvo de interesse apenas dos meus amigos mais nerds :-) Mas vou aproveitar que me interesso bastante pelo tema e vou procurar escrever com freqüência(ou nem tanta assim) sobre o tema, tudo dependerá de como andam as coisas para mim(fim da semana passada e esta agora estão corridinhas!!).
Agora chega de lero-lero e vamos ao que interessa: eu deixei muita coisa "em aberto" no primeiro artigo. Uma pessoa deixou(anonimamente) o comentário dizendo que faltava comentar o código. Bom, quando eu enviei aquele post eu estava com um pouco de pressa(como agora hehehe), mas agora vamos explicá-lo melhor!!
Primeiramente, um pouco do "geral" sobre as bibliotecas do kernel. Obviamente, um sistema operacional requer "rotinas diversas" para uso interno do próprio kernel. São rotinas básicas que usamos em diversos diferentes locais. Um destes exemplos é o printk(), que é um "analogo" ao printf() da libc.
Antes de mais nada, alguns podem ter a dúvida: e porque não utilizar as funções da libc mesmo, que são padronizadas com a linguagem, mais completas e de utilização mais fácil?? Bom, a resposta é simples: o kernel não faz a mínima idéia de que exista uma libc. Quando você está no kernel space só existe o kernel. Bibliotecas, para o ver do kernel, são meros arquivos em formato ELF, referenciados por outros meros arquivos também em formato ELF. A única coisa que o kernel sabe é executar system calls[1] nestes arquivos.
Estas bibliotecas são documentadas naquela API do kernel, cujo link coloquei no outro artigo. Retomando o foco do assunto, no post anterior eu não havia escrito muitos detalhes do código. Revirando meus links, encontrei o link do 'The Linux Kernel Module Programming Guide', que eu não me recordava, mas havia sido minha inspiração na escrita daquele código de exemplo. Todos os detalhes que menciono aqui são semelhantes aos do guia, que é uma das fontes deste artigo, juntamente com os fontes do kernel.
Primeiramente, na estrutura do código, temos nossos dois arquivos: errcon.h e errcon.c. Prezumo que todos que estejam lendo este arquivo conheçam razoavelmente os padrões que comumente são adotados para a divisão de códigos entre arquivos de cabeçalho(.h, com declarações) e arquivos de implementação(os .c ou .cpp no caso de C++). Caso não conheçam, recomendo "googlar" sobre o assunto
Em nosso header, primeiro incluimos alguns headers com declarações de símbolos que utilizaremos. Logo após, definimos algumas "macros úteis". São apenas para concentrar o valor de algumas coisas ali ao começo. Nesta época eu não era tão "avesso" ao uso de macros.
Depois temos nossaas variáveis globais e funções. Quero que atentem bastante às funções 'ec_dev*' e à estrutura 'opers', do tipo file_operations. Elas são as responsáveis pelo funcionamento da parte do arquivo de dispositivo(a entrada no /dev) que nosso módulo nos disponibiliza.
Como vocês podem notar, registramos em uma estrutura file_operations todas as operações relacionadas a arquivo que nosso módulo realiza. Mas neste módulo não utilizamos todas as possibilidades. Mesmo assim, quero que vocês conheçam a declaração da estrutura para verificar as operações disponíveis para manipulação:
struct file_operations {Então, basicamente, registramos as funções que iriamos utilizar(ec_devopen, ec_devclose, ec_devread e ec_devwrite) nos ponteiros de função(open, close, read e write) da estrutura.
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
int (*readdir) (struct file *, void *, filldir_t);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, struct dentry *, int datasync);
int (*aio_fsync) (struct kiocb *, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*dir_notify)(struct file *filp, unsigned long arg);
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
};
Agora, reparem também, ainda no noso arquivo de cabeçalho, que temos um ponteiro para uma declaração do tipo proc_dir_entry. Ela é uma "análoga", se é que podemos dizer isso, da estrutura file_operations. Mais adiante compreenderemos porque ela é declarada na forma de um ponteiro aqui em cima. Por enquanto, adianto apenas que as funções ec_procread e ec_procwrite serão associadas a ela, juntamente com algumas outras informações. Veja declaração da estrutura:
struct proc_dir_entry {Repare que nesta estrutura temos diversos elementos além de ponteiros de funções, diferentemente da file_operations que tinha apenas ponteiros de função(exceto pelo owner, ponteiro que nem utilizamos manualmente).
unsigned int low_ino;
unsigned short namelen;
const char *name;
mode_t mode;
nlink_t nlink;
uid_t uid;
gid_t gid;
loff_t size;
const struct inode_operations *proc_iops;
const struct file_operations *proc_fops;
get_info_t *get_info;
struct module *owner;
struct proc_dir_entry *next, *parent, *subdir;
void *data;
read_proc_t *read_proc;
write_proc_t *write_proc;
atomic_t count; /* use count */
int deleted; /* delete flag */
void *set;
};
Agora vamos finalmente ao nosso arquivo de implementação(errcon.c). A primeira coisa que vemos(após a inclusão do cabeçalho errcon.h) é implementação da função ec_devwrite. O que ela faz, basicamente, é implementar a operação write no dispositivo. Simplesmente lê o buffer de leitura(localizado no user-space, por isso lido com get_user e não diretamente) e o salva em nossa lista dos ultimos 4 erros.
Quanto ao local para onde os ponteiros de nosso array de ponteiro apontam, entenderemos mais tarde. De momento saiba apenas que é para um buffer de dados. Repare que como este módulo é apenas um teste, NENHUM TIPO DE PREVENÇÃO CONTRA BUFFER OVERFLOW FOI TOMADO. Para ocorrer um buffer overflow, basta escrevermos um número de bytes maior do que o tamanho do buffer. E, para este buffer overflow ser mais grave, basta que isso ocorra no ultimo dos 4 buffers, ou então que seja grande o suficiente para passar do fim do ultimo buffer no caso dos outros 3 buffers. Isso tudo se deve a grande preguiça que o autor tem de implementar um contador de caracteres(int i=buf_limit e buf_limit-- dentro do loop :-) hehehe).
Agora prosseguindo, temos nossa função ec_procread, que responde à operação read do arquivo de dispositivo. Ela basicamente escreve os dados do buffer do módulo(com a mensagem do ultimo erro recebido pelo módulo) no buffer do user-space(lá aonde o processo está fazendo a leitura. A "idéia" do put_user é a mesma do get_user, com diferença apenas operacional(um coloca no usuário e o outro pega do usuário, :-) entenda este comentário como quiser).
Agora vamos para a ec_open. Ela é responsável pela abertura do arquivo (citação de Dr. Obvio). Para evitar multiplos processos efetuando I/O no módulo, fazemos um "controle de abertura". No mais, a função também reseta o buffer de leitura. Como vocês viram, na ec_read nós apenas liamos apartir do buffer. Mas aí ficava no ar a pergunta: daonde vinha a posição correta do buffer?
É simples. Imagine que o processo não leia todos os dados de uma vez só. Suponhamos que nós tenhamos uma string de erro "hahehihohu". O tamanho de buffer utilizado pelo processo que lê o dispositivo é 2. Acompanhe a seqüência:
- Processo efetua open() no dispositivo;
- Processo lê 2 bytes;
- Processo "trabalha" os 2 bytes lidos;
- Processo lê mais 2 bytes;
- Processo "trabalha" mais 2 bytes lidos;
- Processo lê mais 2 bytes;
- ...
Só para constar, vejamos a linha aonde fazemos a chamada:
try_module_get(THIS_MODULE);Nesta chamada simplesmente incrementamos o contador de uso do módulo, gerenciado pelo kernel. Não confunda com nosso contador interno(ec_openc), que é para uso dentro do módulo. Ao executarmos um comando lsmod no shell, veremos uma tela similar a esta:
bruno@bw1:~$ lsmod
Module Size Used by
parport_pc 27812 0
parport 34760 1 parport_pc
yenta_socket 27148 1
Repare no terceiro campo(Used By), aonde vemos um número, algumas vezes seguido de uma lista de módulos. Este número é o contador de usos. Os módulos listados são outros módulos que utilizam recurso do listado. Isso significa que o try_module_get não serve apenas para indicar quantas vezes o arquivo de dispositivo foi aberto, mas para n outras coisas.
A função ec_devclose dispensa comentários. Apenas esclareço que o try_module_put decrementa o contador de uso do módulo. Na função ec_procread, simplesmente escrevemos no buffer uma mensagem formatada, dizendo o número de erros e o ultimo erro que foram registrados. Na ec_procwrite, utilizamos a função copy_from_user para ler 1 caractere do buffer recebido. Caso este caractere lido seja 'c', é efetuado um loop percorrendo o buffer de mensagens de erro e apagando seus dados.
Bom, agora que todos já entenderam "cada pedaço" da coisa, vamos ao "ponto de junção", aonde a gente "junta toda essa cosia e faz virar uma só". É o nosso "main 1", o init_module. Como eu já havia citado anteriormente, este "cara" é o responsável por disponibilizar aquilo que o módulo fornece, e inicializar o "serviço" do módulo.
A primeira coisa que ele faz é preparar os nossos buffers. Como você pode ver no código, de acordo com minha explicação sobre ponteiros, criamos um array global, de 16386 bytes(de acordo com a macro EC_PROCFILE_BUFF_SIZE) apontado por ec_errors, ainda no nosso header(errcon.h). Agora nós dividimos a área apontada por ec_errors em 4 partes, cada uma com seu início apontado por um dos ponteiros do array de ponteiros apontado por ec_error(esta frase é meio confusa, mas tente reler umas 2 vezes, ou se tiver raciocínio visual e precisar, não tenha vergonha de desenhar num papel ao longo da leitura para entender a frase - e se ainda não entender, recomendo ler novamente a explicação sobre ponteiros). Observe que temos 2 poréms nesta história: o primeiro é que ao longo do programa não utilizamos nenhuma mensagem além da ultima. Isso foi uma falha minha, pois na época eu não tinha noção alguma do que significava "requisito de software".
O segundo porém, é um pequeno errinho meu. Se vocês acompanharem o loop aonde é feita essa divisão da área, vocês vão ver que todos os 4 ponteiros apontam para a mesma área, que seria "a segunda posição" das 4 na qual dividimos. Apartir de agora, quando vocês olharem o código ele estará atualizado, e o valor:
(EC_PROCFILE_BUF_SIZE / 4)será multiplicado por i.
Após dividirmos o nosso array em 4 buffers "distintos", criamos nossa entrada no sistema de arquivos proc. Fazemos um tratamento de erro, e caso ocorra realmente algo errado, nós efetuamos a tentativa de remoção da entrada recém criada(ou não criada, não temos como ter certeza, removemos para evitar uma entrada desagradável e permanente no /proc).
Logo depois, criamos nossa entrada no /dev. Só para constar, caso o retorno do register_chrdev seja negativo(erro) em vez do major, temos certeza de que o device não foi criado(diferentemente da entrada no /proc). E, então, removemos a entrada.
Em ambos os tratamentos de erro, retornamos negativo. Ou seja: indicamos pro kernel que ocorreram erros e ele deve cancelar o carregamento do módulo. O kernel, por sua vez, informa o modutils disso, e este por sua vez joga uma mensagem de erro para o usuário. É vital fazermos isso para evitar problemas!
Observem a diferença entre as abordagens da criação do arquivo de dispositivo e da criação da entrada no /proc. No primeiro caso, criamos a estrutura com as informações das operações de arquivo, e depois chamamos a função responsável "registrando" esta estrutura. No segundo, criamos apenas um ponteiro e utilizamos uma função que retorna a área da estrutura. Nesta função, passamos apenas o nome do arquivo a ser criado.
Uma vez registradas, estas estruturas podem ser modificadas a qualquer momento. É o que fazemos com a estrutura do nosso arquivo no /proc. Ao remover, passamos como parâmetro o endereço de proc_root. A entrada proc_root nada mais é do que outra estrutura proc_dir_entry, que representa a raiz do /proc.
Veja que depois nós modificamos nossa entrada no /proc acessando diretamente a estrutura. Logo após isso, o restante do código é apenas complementar: imprimimos com printk as mensagens informando alguns detalhes do módulo.
Retornamos 0(o valor de SUCCESS) para indicar sucesso na operação. Valores negativos indicam erro e cancelam o carregamento do módulo.
Quase no fim, temos nosso cleanup_module. Basicamente, ele desregistra nosso dispositivo de caractere, e remove nossa entrada do /proc.
Ao fim do código, utilizamos algumas macros para registrar algumas informações sobre o módulo: sua licensa, seu autor, o nome de seu dispositivo, e sua descrição. Você pode visualizar suas informações com o comando:
$ modinfo errcon.ko
Finalmente, peço desculpas pela demora a postar algo novo. É que esta semana está corrida!! Espero que gostem, em breve mais conteúdo sobre o kernel!!
OBS: se alguém quiser que eu escreva sobre algum tema específico, e estiver ao meu alcance, é só dizer que se tiver no meu domínio e se encaixar no meu "time" eu escrevo.
Agradeço a atenção de todos os meus leitores. Caso não saibam, tem um milhão lendo agora esse blog. E em seguida esse milhão vai ser cozido :P
[1] System Calls, ou chamadas de sistema, são "funções" disponibilizadas pelo sistema operacional aos aplicativos. Geralmente bem básicas. Vide man page for syscalls(2)
Classificação do conteúdo: SÉRIO
Sobre mim:
Curriculum Vitae
Site Pessoal
às
Terça-feira, Outubro 28, 2008
0
ociosos comentaram
Links para esta postagem
O Inútil que escreveu foi
"Brunildz" Bruno Moreira Guedes
Marcadores:
C/C++,
Kernel,
Linux,
Sistemas Operacionais
sábado, 25 de outubro de 2008
DICA: Eliminando Warnings de Seus Programas
Palavras-chave: C/C++, GCC, Dicas
Constatei vendo meus diversos amigos(colegas e ex-colegas de trabalho, colegas de faculdade, conhecidos diversos) programando: a esmagadora maioria dos programadores IGNORA os warnings do compilador.
As razões disso parecem ter duas origens. A primeira delas é a 'omissão', aquela idéia de "ahh, compilou, não dá nada". Neste caso, o indivíduo deveria se auto considerar desleixado, e dependendo do que ele estiver desenvolvendo, irresponsável. Quando falamos de C ou C++, aonde lidamos com mais elementos de "não tão alto nível assim" - como ponteiros e assembly inline, essa omissão se torna mais grave ainda.
Agora todos que leram até aqui, se ainda não eram concientes agora são(eu espero). Sendo assim, vamos para a segunda causa. Você está na correria. Seu programa gerou alguns warnings. Apenas por mal costume(e todos nós temos), você 'deixa para depois' corrigir aquele warning. Aí o dia que você vai corrigir os warnins deixados para depois, você se surpreende: apenas 800 warnings para corrigir. Nada que 3 meses de trabalho não resolvam, pouquinha coisa!!
E lá vai o dito programador corrigindo warning por warning, e contando eles novamente... 800... 799... 798... ... ... 0. Terminou, warnings zerados. Aí você pensa: agora eu não deixo mais para depois. Aí o primeiro momento de "correria com prazos" você deixa 1 pra depois. E ainda diz para sí mesmo: é só 1, os outros não vou deixar.
Nós, seres humanos, insistimos nessas coisas. Mas é fato, não adianta você dizer para sí mesmo que vai deixar só aquele para depois. Sempre aparecem outras excessões.
Sendo assim, aqui vai uma dica para quem utiliza o GCC. Se não funciona por nossa própria consciência, então o compilador que nos obrigue!! Existem diversos parâmetros do compilador relacionados a Warnings, mas eu só vou falar de dois.
O primeiro é o -Wall, que manda o compilador registrar e exibir todos os warnings. O segundo é o -Werror, que faz com que warnings sejam tratados como erros. Com esses dois parâmetros juntos você não tem escapatória: terá que corrigir seu warning, ou o programa não compila. Veja a linha de comando de exemplo:
$ gcc -Wall -Werror -o binario source.cpp
Pronto. Assim seu código é mais confiável, e você não paga nada a mais por isso. Espero que todos gostem deste artigo!!
[]'s
Classificação do conteúdo: SÉRIO
Sobre mim:
Curriculum Vitae
Site Pessoal
Constatei vendo meus diversos amigos(colegas e ex-colegas de trabalho, colegas de faculdade, conhecidos diversos) programando: a esmagadora maioria dos programadores IGNORA os warnings do compilador.
As razões disso parecem ter duas origens. A primeira delas é a 'omissão', aquela idéia de "ahh, compilou, não dá nada". Neste caso, o indivíduo deveria se auto considerar desleixado, e dependendo do que ele estiver desenvolvendo, irresponsável. Quando falamos de C ou C++, aonde lidamos com mais elementos de "não tão alto nível assim" - como ponteiros e assembly inline, essa omissão se torna mais grave ainda.
Agora todos que leram até aqui, se ainda não eram concientes agora são(eu espero). Sendo assim, vamos para a segunda causa. Você está na correria. Seu programa gerou alguns warnings. Apenas por mal costume(e todos nós temos), você 'deixa para depois' corrigir aquele warning. Aí o dia que você vai corrigir os warnins deixados para depois, você se surpreende: apenas 800 warnings para corrigir. Nada que 3 meses de trabalho não resolvam, pouquinha coisa!!
E lá vai o dito programador corrigindo warning por warning, e contando eles novamente... 800... 799... 798... ... ... 0. Terminou, warnings zerados. Aí você pensa: agora eu não deixo mais para depois. Aí o primeiro momento de "correria com prazos" você deixa 1 pra depois. E ainda diz para sí mesmo: é só 1, os outros não vou deixar.
Nós, seres humanos, insistimos nessas coisas. Mas é fato, não adianta você dizer para sí mesmo que vai deixar só aquele para depois. Sempre aparecem outras excessões.
Sendo assim, aqui vai uma dica para quem utiliza o GCC. Se não funciona por nossa própria consciência, então o compilador que nos obrigue!! Existem diversos parâmetros do compilador relacionados a Warnings, mas eu só vou falar de dois.
O primeiro é o -Wall, que manda o compilador registrar e exibir todos os warnings. O segundo é o -Werror, que faz com que warnings sejam tratados como erros. Com esses dois parâmetros juntos você não tem escapatória: terá que corrigir seu warning, ou o programa não compila. Veja a linha de comando de exemplo:
$ gcc -Wall -Werror -o binario source.cpp
Pronto. Assim seu código é mais confiável, e você não paga nada a mais por isso. Espero que todos gostem deste artigo!!
[]'s
Classificação do conteúdo: SÉRIO
Sobre mim:
Curriculum Vitae
Site Pessoal
às
Sábado, Outubro 25, 2008
2
ociosos comentaram
Links para esta postagem
O Inútil que escreveu foi
"Brunildz" Bruno Moreira Guedes
Marcadores:
Dicas,
GCC
Assinar:
Postagens (Atom)
