Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Log writer 기능 추가 및 보수 #62

Merged
merged 11 commits into from
Nov 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.whoz_in.log_writer;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.TimeUnit;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class TimeLogger {
@Scheduled(fixedDelay = 60000, timeUnit = TimeUnit.MINUTES)
public void log(){
System.out.println(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.whoz_in.log_writer.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.whoz_in.log_writer.managed.ManagedInfo;
import com.whoz_in.log_writer.monitor.MonitorInfo;
import java.io.IOException;
import java.util.List;
import java.util.Map;
Expand All @@ -11,61 +13,81 @@
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Component;

//실행시킬 프로세스들이 필요로 하는 정보를 제공하는 역할을 한다.
//이를 위해 network-<profile>.json에서 설정값을 가져오며, 올바른 값인지 검증한다.
@Getter
@Component
public class NetworkConfig {
// Getters and setters
private final Map<String, String> ssidInfo; //k: name, v: ssid
private final Monitor monitor;
private final List<Managed> mdnsList;
private final List<Managed> arpList;
private final MonitorInfo monitorInfo;
private final List<ManagedInfo> mdnsList;
private final List<ManagedInfo> arpList;

@SuppressWarnings("unchecked")
public NetworkConfig(@Value("${spring.profiles.active:default}") String profile, ResourceLoader loader, ObjectMapper mapper) {
Resource resource = loader.getResource("classpath:/network-%s.json".formatted(profile));
String jsonPath = "classpath:/network-%s.json".formatted(profile);
Resource resource = loader.getResource(jsonPath);
Map<String, Object> map;

try {
//JSON 파일 읽기
Map<String, Object> map = mapper.readValue(resource.getFile(), Map.class);
map = mapper.readValue(resource.getFile(), Map.class); //JSON 파일 읽기
} catch (IOException e) {
throw new RuntimeException(jsonPath + " 로드 실패");
}

try {
//interface_info
List<Map<String, String>> interfaceInfoList = (List<Map<String, String>>) map.get("ssid_info");
List<Map<String, String>> interfaceInfoList = (List<Map<String, String>>) map.get(
"ssid_info");
this.ssidInfo = interfaceInfoList.stream()
.collect(Collectors.toMap(info-> info.get("name"), info->info.get("ssid")));

.collect(Collectors.toMap(info -> info.get("name"), info -> info.get("ssid")));
//monitor
Map<String, String> monitorMap = (Map<String, String>) map.get("monitor");
this.monitor = new Monitor(
this.monitorInfo = new MonitorInfo(
monitorMap.get("interface"),
generateCommand(monitorMap.get("command"), monitorMap.get("interface"))
);

// managed
Map<String, Object> managedMap = (Map<String, Object>) map.get("managed");

// mdns
Map<String, Object> mdnsMap = (Map<String, Object>) managedMap.get("mdns");
String mdnsCommand = (String) mdnsMap.get("command");
this.mdnsList = ((List<String>) mdnsMap.get("interfaces")).stream()
.map(interfaceName->new Managed(interfaceName, ssidInfo.get(interfaceName), generateCommand(mdnsCommand, interfaceName)))
.map(interfaceName -> new ManagedInfo(interfaceName,
ssidInfo.get(interfaceName),
generateCommand(mdnsCommand, interfaceName)))
.toList();

// arp
Map<String, Object> arpMap = (Map<String, Object>) managedMap.get("arp");
String arpCommand = (String) arpMap.get("command");
this.arpList = ((List<String>) arpMap.get("interfaces")).stream()
.map(interfaceName->new Managed(interfaceName, ssidInfo.get(interfaceName), generateCommand(arpCommand, interfaceName)))
.map(interfaceName -> new ManagedInfo(interfaceName,
ssidInfo.get(interfaceName),
generateCommand(arpCommand, interfaceName)))
.toList();

} catch (IOException e) {
throw new RuntimeException("Failed to load configuration from JSON file", e);
validate();
} catch (Exception e){
e.printStackTrace();
throw new RuntimeException("network json 구조가 잘못되었음");
}
}

private String generateCommand(String commandTemplate, String interfaceName) {
return commandTemplate.replace("{{interface}}", interfaceName);
}

public record Monitor(String interfaceName, String command) {}
public record Managed(String interfaceName, String ssid, String command) {}
private void validate(){
this.mdnsList.forEach(mdns-> {
if (!ssidInfo.containsKey(mdns.interfaceName()))
throw new IllegalArgumentException("mdns에 정의되지 않은 인터페이스가 있음");
});
this.arpList.forEach(arp-> {
if (!ssidInfo.containsKey(arp.interfaceName()))
throw new IllegalArgumentException("arp에 정의되지 않은 인터페이스가 있음");
});
//TODO: 더 많은 검증
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.PropertySources;

@Configuration
@PropertySources({@PropertySource("classpath:/env.properties")})
@PropertySource("classpath:/env.properties")
public class PropertiesConfig {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.whoz_in.log_writer.managed;

//Managed 프로세스를 생성하고 처리하는 과정에서 필요한 정보를 담는다.
public record ManagedInfo(String interfaceName, String ssid, String command) {}
Original file line number Diff line number Diff line change
@@ -1,29 +1,34 @@
package com.whoz_in.log_writer.managed.arp;

import com.whoz_in.log_writer.managed.ManagedInfo;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;

//단발성 프로세스
public final class ArpLogProcess {
public class ArpLogProcess {
private final BufferedReader br;
public ArpLogProcess(String command, String password) {
Process process;
private final Process process;
public ArpLogProcess(ManagedInfo info, String password) {
try {
process = new ProcessBuilder(command.split(" ")).start();
br = new BufferedReader(new InputStreamReader(process.getInputStream()));
//TODO: error 처리 로직 수정
new File("../error").mkdir(); //에러 처리 수정하면 이거 없앨게요..
this.process = new ProcessBuilder(info.command().split(" "))
.redirectError(new File("../error", info.ssid()+".txt")) //이것도..
.start();
this.br = new BufferedReader(new InputStreamReader(process.getInputStream()));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
bw.write(password);
bw.newLine();
bw.flush();
} catch (IOException e) {
throw new RuntimeException(e);
}

}

//단발성 프로세스로, 프로세스가 종료될 때까지 run은 블로킹된다.
Expand All @@ -42,4 +47,7 @@ public List<String> readLines(){
return logs;
}

public boolean isAlive(){
return process.isAlive();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@


import com.whoz_in.log_writer.config.NetworkConfig;
import com.whoz_in.log_writer.config.NetworkConfig.Managed;
import com.whoz_in.log_writer.managed.ManagedInfo;
import com.whoz_in.log_writer.managed.ManagedLog;
import com.whoz_in.log_writer.managed.ManagedLogDAO;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
Expand All @@ -16,7 +17,7 @@
public class ArpLogWriter {
private final ManagedLogDAO dao;
private final ArpLogParser parser;
private final List<Managed> arpList;
private final List<ManagedInfo> arpList;
private final String sudoPassword;

public ArpLogWriter(ManagedLogDAO dao,
Expand All @@ -30,22 +31,35 @@ public ArpLogWriter(ManagedLogDAO dao,
this.sudoPassword = sudoPassword;
}

@Scheduled(fixedRate = 5000)
@Scheduled(initialDelay = 10000, fixedRate = 5000)
private void scan() {
Set<ManagedLog> logs= arpList.stream()
.flatMap(arp-> {
ArpLogProcess proc = new ArpLogProcess(arp.command(), sudoPassword); //프로세스 실행
List<ManagedLog> logs= arpList.stream()
.flatMap(arpInfo-> {
ArpLogProcess proc = new ArpLogProcess(arpInfo, sudoPassword); //프로세스 실행
List<String> lines = proc.readLines(); //프로세스의 모든 출력 가져오기
return lines.stream() //출력 라인들을 ManagedLog 변환하며 ssid도 넣어줌
Set<ManagedLog> procLogs = lines.stream() //출력 라인들을 ManagedLog 변환하며 ssid도 넣어줌
.filter(parser::validate)
.map(line->{
ManagedLog log = parser.parse(line);
log.setSsid(arp.ssid());
log.setSsid(arpInfo.ssid());
return log;
});
})
.collect(Collectors.toSet()); //Set으로 중복 제거

/*
Arp-scan은 단발성인데
Process의 isAlive()는 실행 중일 때도 false일 수 있고, 종료 중일 때도 true일 수 있으므로 오류의 판단이 힘들다.
따라서 Arp-scan의 경우 무조건 1개 이상의 결과가 나오므로 0개라면 실행 실패라고 판단한다.
*/
if (procLogs.isEmpty()) {
System.err.println("[managed - arp(%s)] 실행 실패 : ERROR".formatted(arpInfo.ssid()));
return Stream.empty();
}
System.out.println("[managed - arp(%s)] 저장할 로그 개수 : %d".formatted(arpInfo.ssid(), procLogs.size()));
return procLogs.stream();
})
.collect(Collectors.toSet()); //Set으로 중복 제거
System.out.println("[managed - arp] 저장할 로그 개수 : " +logs.size());
.toList();

dao.insertAll(logs);
}
}
Original file line number Diff line number Diff line change
@@ -1,38 +1,45 @@
package com.whoz_in.log_writer.managed.mdns;

import com.whoz_in.log_writer.common.util.NonBlockingBufferedReader;
import com.whoz_in.log_writer.managed.ManagedInfo;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Writer;

public final class MdnsLogProcess {
public class MdnsLogProcess {
private final Process process;
private final NonBlockingBufferedReader cbr;

public MdnsLogProcess(String command, String sudoPassword) {
//TODO: Info로 변경
public MdnsLogProcess(ManagedInfo info, String sudoPassword) {
try {
this.process = new ProcessBuilder(command.split(" "))
new File("../error").mkdir(); //에러 처리 수정하면 이거 없앨게요..
this.process = new ProcessBuilder(info.command().split(" "))
.redirectError(new File("../error", info.ssid()+".txt"))
.start();
//TODO: errorStream 로깅 - process.getErrorStream()
this.cbr = new NonBlockingBufferedReader(new BufferedReader(new InputStreamReader(process.getInputStream())));

Writer writer = new OutputStreamWriter(process.getOutputStream());
this.cbr = new NonBlockingBufferedReader(new BufferedReader(new InputStreamReader(this.process.getInputStream())));
Writer writer = new OutputStreamWriter(this.process.getOutputStream());
writer.write(sudoPassword + System.lineSeparator());
writer.flush();
} catch (IOException e) {
throw new RuntimeException(command + " 실행 실패");
throw new RuntimeException(info.command() + " 실행 실패");
}
}

/**
*
* @return 프로세스의 출력에서 한 줄을 읽어들인다.
* 읽을 줄이 없을경우 null을 출력한다.
*/
public String readLine() throws IOException {
return cbr.readLine();
public String readLine(){
//여기서 try catch
try {
return this.cbr.readLine();
} catch (IOException e) {
throw new RuntimeException(e);
}
}

public boolean isAlive(){
Expand Down
Loading